Python displays multiple return values

I am using pythons mock.patch and would like to change the return value for each call. Here is a caution: the patch function has no inputs, so I cannot change the return value based on input.

Here is my code for reference.

def get_boolean_response(): response = io.prompt('y/n').lower() while response not in ('y', 'n', 'yes', 'no'): io.echo('Not a valid input. Try again']) response = io.prompt('y/n').lower() return response in ('y', 'yes') 

My test code is:

 @mock.patch('io') def test_get_boolean_response(self, mock_io): #setup mock_io.prompt.return_value = ['x','y'] result = operations.get_boolean_response() #test self.assertTrue(result) self.assertEqual(mock_io.prompt.call_count, 2) 

io.prompt is just a platform-independent (python 2 and 3) version of the "input". Therefore, in the end, I try to mock users. I tried using a list for the return value, but this does not work.

You can see that if the return value is something invalid, I will just get an infinite loop here. So I need a way to ultimately change the return value, so my test actually ends.

(another possible way to answer this question might be to explain how I could simulate user input in a unit test)




Do not duplicate this question mainly because I do not have the ability to change the input.

One of the comments from the answer to this question corresponds to the same lines, but no answer / comment was provided.

+62
python unit-testing mocking python-mock
Jul 22 '14 at 20:25
source share
1 answer

You can assign iterable to side_effect , and the layout will return the next value in the sequence each time it is called

 >>> from unittest.mock import Mock >>> m = Mock() >>> m.side_effect = ['foo', 'bar', 'baz'] >>> m() 'foo' >>> m() 'bar' >>> m() 'baz' 

Quoting the Mock() documentation :

If side_effect is iterable, each layout call returns the next value from the iterable.

As an aside, the response is not 'y' or 'n' or 'yes' or 'no' test response is not 'y' or 'n' or 'yes' or 'no' will not work; you ask if the expression is true (response is not 'y') , or 'y' true (always the case, a non-empty string is always true), etc. Different expressions on both sides of the or operators are independent. See How to check one variable for several values?

You should also not use is to test the string. The CPython interpreter can reuse string objects under certain circumstances , but this is not the behavior you should rely on.

As such, use:

 response not in ('y', 'n', 'yes', 'no') 

instead; this will use equality tests ( == ) to determine if response refers to a string with the same content (value).

The same applies to response == 'y' or 'yes' ; use response in ('y', 'yes') instead.

+117
Jul 22 '14 at 20:34
source share



All Articles