Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does the Mockito Verify method works?

I was searching how to test that something was properly deleted from a database and I found this answer: https://stackoverflow.com/a/38082803/9115438 but it got me thinking,what if the delete method fails and it doesn't actually delete the object,what then? Does the verify method only checks if the delete method was called once or that it was called once and it was succesful? Because if it doesn't check that the delete was succesful then that test is not useful at all.

like image 652
Aintsmartenough Avatar asked Mar 03 '23 17:03

Aintsmartenough


1 Answers

Your point is relevant.
Mockito.verify() will just verify that an invocation was done on a mock during the method execution.
It means that if the real implementation of the mocked class doesn't work as it should, the test is helpless.
As a consequence, a class/method that you mock in a test have also to be unitary tested.
But does it mean that verify() is always fine ? Not really.

Suppose a method to test that deletes something :

public class Foo{
    MyDao myDao;

    public void delete(int id){
       myDao.delete(id);
    }
}

The test passes :

@Mock
MyDao myDaoMock;
Foo foo;

@Test
public void delete(int id){
   foo.delete(id);
   Mockito.verify(myDaoMock).delete(id);
}

Suppose now I change the implementation as :

public void delete(int id){
   myDao.delete(id);
   myDao.create(id);
}

The test is still green... Oooops.

Other scenario, suppose a method that mainly invokes dependencies methods :

public void doThat(){
   Foo foo = fooDep.doThat(...);
   Bar bar = barDep.doThat(foo);
   FooBar fooBar = fooBarDep.doThat(foo, bar);
   fooBis.doOtherThing(...);
   // and so for
}

With the verifying approach, the unit test will just describe/copy past the implementation of your method in a Mockito format.
It asserts nothing in terms of returned result. Changing the implementation in a bad way (adding an incorrect invocation or removing a required invocation) is hard to detect with a test failure like the test becomes just a reflect of the invoked statements.

Mock verifying is often something to use with cautious.
In some specific cases, it may be helpful but in many cases, I have seen developers abuse of that (75% or more of the unit test class is mock setup/record/verify) and as a consequence it produces a bloat unit test with few value, very hard to maintain and that also slows down your builds for not fair reasons.
In fact, for methods to test that rely essentially on functions with side effects, integration tests (even sliced/partial) should be favored.


Mocks Aren't Stubs of Martin Fowler is an excellent post that should interest you.

This has particularly struck me when I've observed a mockist programmer. I really like the fact that while writing the test you focus on the result of the behavior, not how it's done. A mockist is constantly thinking about how the SUT is going to be implemented in order to write the expectations. This feels really unnatural to me.

While this Martin Fowler post is interesting, I don't agree with all either.
I agree that a mockist approach where developers don't mock a dependency because the dependency is annoying but does that systematically is generally a bad idea.
We should always have a good reason to introduce a mock such as :

  • external system dependency
  • invocation slowness
  • reproducibility issue
  • complex setup to interact with the dependency (input and or output)

But I disagree that creating explicitly stubs is generally a good idea because the stub code takes time to write, may have bugs and we will have to maintain that. At last to make things clean robust, stubs classes should also be unitary tested. All that has a cost.
On the other hand, mocks produced by mocking libraries don't have all these defects.
And nothing prevents us from using these mocks with a stub mind : make the mocks to collaborate as stubs, that is :

  • yes for input/output recording
  • yes for few and relevant verify()
  • but no for verify() abuses
  • and no for multiplication of fine grained mock behavior recording that couples the test to the implementation
like image 181
davidxxx Avatar answered Mar 13 '23 04:03

davidxxx