Re: Single assert / Single mock rule in practice

Adam Sroka <[email protected]> Wed, 15 Jan 2014 15:32:27 -0500
Newsgroups gmane.comp.programming.test-driven-development
Message-ID <CALaPUVeCRKHOFH26T_+p-zMH1ssUNtG8bt+hH66vW3zLuTmSmg@mail.gmail.com>
P.S. in the first sentence of that second paragraph I should have said you
stub the method and assert something about the SUT, only. In other words:

For a query:
Arrange: stub the query and pass the stub to the object under test
Act: call the method on the object under test that will cause the stubbed
method to be queried
Assert: assert something about the object under test that should only be
true if the stubbed method returns correctly

For a command:
Arrange: create a mock and pass it to the object under test *
Act: call the method on the object under test that will cause the method on
the mock to be called
Assert: verify that the mock was called correctly which should only be true
if the object under test did what we expected

* depending on the framework you may also have to specify how the mock is
expected to be called before you call it.



On Wed, Jan 15, 2014 at 3:20 PM, Adam Sroka <[email protected]> wrote:

> Are you familiar with Command Query Separation (CQS)? I've always found
> that the most coherent and useful way to explain why you would choose a
> mock or a stub. The most interesting thing about a method is either what it
> returns or, in the case where you don't care what it returns, whether it
> was called with the correct semantics, not both.
>
> So, if the SUT cares about the value that a method returns then you stub
> that method, only. If the SUT doesn't care about the value a method returns
> then you use a mock and verify the call has the expected semantics, only.
>
> Does that help?
>
>
>
> On Tue, Jan 14, 2014 at 6:37 AM, Mateusz Łoskot <[email protected]>wrote:
>
>>
>>
>> Hi,
>>
>> I'd like to ask for ideas of best practices in case of developing and
>> testing
>> a wrapper for a Python legacy module.
>> Especially, about applying the single assert / single mock rule
>> which I have been learning about from numerous books/videos/blogs.
>>
>> I have Python xdep module which exposes API in form of bunch of free
>> functions.
>> I'm creating new Python module foo which uses xdep (the idea is to provide
>> simplified, Pythonic interface, but that is not directly relevant to
>> my question here).
>>
>> I'm trying to keep the code as simple & generic as possible, so my
>> question is
>> not about Python but unit testing and mocking.
>>
>> # xdep.py
>> def open():
>> ...
>> def get_item(item_id):
>> ...
>> def close():
>> ...
>>
>> # foo.py
>> import xdep
>>
>> def exists(item_id):
>> xdep.open()
>> item = xdep.get_item(item_id)
>> xdep.close()
>> if item:
>> return item.id == item_id
>> else:
>> return false
>>
>> Here is my module to unit test behaviuour of the foo:
>> - I create and plug a stub for the legacy module xdep
>> - I mock functions of xdep
>>
>> # test_foo.py
>> import unittest
>> import unittest.mock as mock
>>
>> class ItemTest(unittest.TestCase):
>> def setUp(self):
>>
>> # create stub for external dependency of foo
>> self.xdep_stub = mock.Mock()
>> patcher = mock.patch.dict('sys.modules', {'xdep': self.xdep_stub})
>> self.addCleanup(patcher.stop)
>> patcher.start()
>>
>> # import foo does import xdep, as patched above
>> import foo
>> self.foo = foo
>>
>> def test_exists_ExistingItemId_ReturnTrue(self):
>> foo = self.foo
>> # Arrange
>> ### Data
>> item_id = 1
>> ### Mock
>> foo.xdep.get_item.return_value = 1
>> # Act
>> item_exists = foo.item.exists(item_id)
>> # Assert
>> self.assertTrue(item_exists)
>>
>> I have a single assertion and I use single mock which mocks xdep.get_item
>> function, so it seems a valid yet clear unit test.
>>
>> Now, having the single assert/single mock rule in mind, I have some
>> questions,
>> about the test_exists_ExistingItemId_ReturnTrue in particular:
>>
>> 1) How asserting on foo.item.exists return value is different from
>> asserting how xdep.get_item mock was called?
>>
>> I could have replaced
>> self.assertTrue(item_exists)
>> with
>> foo.xdep.get_item.assert_called_once_with(1)
>>
>> 2) What about having both assertions, would that break the single
>> assertion rule?
>>
>> self.assertTrue(item_exists)
>> foo.xdep.get_item.assert_called_once_with(1)
>>
>> I'm not concern about breaking the rule as technical convention, but as:
>> am I testing multiple things here?
>>
>> 3) AFAIU, it's natural that unit tests are coupled with implementation of
>> method/behaviour they are testing, foo.exists function in my case.
>>
>> So, given that foo.exists function calls three functions of the legacy
>> xdep module
>> xdep.open
>> xdep.get_item
>> xdep.close
>> should I mock all the three function and verify how they were called?
>> Would that still belong to scope of test_exists_ExistingItemId_ReturnTrue
>> test
>> or I better create a new dedicated test case(s), something like
>> test_exists_IfCalled_xdepOpenCalled
>> test_exists_IfCalled_xdepCloseCalled
>> test_exists_IfCalled_xdepGetItemCalled
>> and do assert_called_once_with() appropriately.
>>
>> Above, I count each mock for each xdep function as a separate mock,
>> so "single mock per test" rule seems to suggests I should verify they are
>> called
>> in separate tests, doesn't it?
>>
>> Also, some of other xdep functions need to be called with multiple
>> parameters,
>> and as setting up and verifying their mocks may be quite elaborate,my
>> gut feeling
>> tells me that single unit test per "legacy API call expectation" as
>> listed above
>> is a good approach for readability and maintainability of tests.
>> Am I right?
>>
>> 4) What other, if any, approach to single assert/single mock rules
>> would you take for testing similar case as above?
>>
>> Best regards,
>> --
>> Mateusz Łoskot, http://mateusz.loskot.net
>>  
>>
>
>