Karan
Karan

Reputation: 15104

What is the point of testing whether a mocked method has been called?

Okay, so this might be a dangerous question to ask. I have been doing unit testing for a while, but for some reason, I woke up this morning and I asked myself this question.

Say I have an interface UserFactory, and it has the method CreateUser.

At some point I need to create a user right? So I create a test checking whether CreateUser was called for the UserFactory at the appropriate place.

Now, unit tests are pretty coupled to the actual code - which is fine. But maybe a bit too much? As in, the only way to break the test is by not calling the call to CreateUser. We are not checking its implementation etc. but just checking the interface has been called. But whoever does remove that call, would have a failing test and ultimately remove the verify statement from the step to verify the CreateUser was called.

I have seen this happen over and over again.

Could someone bring the light back to me and explain why is it beneficial to verify mocked objects' methods have been called? I can see why it may be useful to set them up, say CreateUser should return a dummy user for later part of the code, but in places where we are simply and only verify if they have been called is the part that got me.

Thanks!

Upvotes: 7

Views: 236

Answers (4)

k.m
k.m

Reputation: 31464

It's important to realize what purpose dependencies serve in tested code:

  • help fulfilling class contract/responsibility (like your UserFactory example)
  • allow to delegate work that's beyond class scope, but has to be done (for example, logging functionality might be such case)

Now, if dependency (mock) fails to help, obviously tested code that depends on this help will fail too. In such cases, having extra test to verify that dependency was called, is not very beneficial. No call will result in multiple failures in tests that depend on it with clear message.

Going back to your factory example, what happens when it's not called? Some other tests, probably those verifying whether that user was saved somewhere, or that something was made with it will fail.

There's naturally second group of dependencies, those that don't affect your code at all, but rather perform silently in the background. Those however, will be most likely reflected in your code responsibilities, say:

SaveUser method should save new user in repository and log operation result

Usually, there's no other way to do behavior-check than verifying whether appropriate method was called.

As a conclusion, two questions to consider when determining whether you should write a test:

  • will this test verify (part of) responsibility of my class?
  • what knowledge about tested code will I gain when this test passes/fails?

Unless it's necessary, don't test that your code calls other code; test that your code works in a way you assume it does.

Upvotes: 1

Augusto
Augusto

Reputation: 29977

Verifying a mocked object is often a necessary evil and, as you mentioned, unit tests sometimes are really tightly coupled to the class under test.

I don't know how to give a good answer to your question, but I'll try.

Take this bit of code as an example, where userRepository is a dependency (the example is not exactly great).

public void doSomething(User user) {
    if( user.isValid() ) {
        userRepository.save(user)
    } else {
        user.invalidate();
    }
}

One way to test this, would be to plug the real repository, which connects to the DB, and verify that the user is persisted. But since I'm unit testing, I can't have an external dependency in my test.

Now, which other options do you have to verify the scenario where the user is valid? Since userRepository.save() returns void, the only way is to verify the side effect is to verify that the mock was called. If I wouldn't verify the mock, then the unit test wouldn't be very good, as I could delete the line where I save the object, and the test would still pass.

This is not the case with some mocks that return a value, and then than value is used in the method. This usually means that if the mock returns a null, then the application throws a NullPointerException (in the case of java).

Upvotes: 2

djna
djna

Reputation: 55937

The only way to break the test is by not calling CreateUser

So in method with a bit of complexity, some conditionals etc, it could be quite easy to skip that call; later maintenance could unintentionally result in the call being missed.

So I think these tests for side effects can have value. In your case, should CreateUser always be called? What about when exceptions are thrown? There can be values in checking that CreateUser is not called in some conditions.

I do agree with you that in simple situations it sometimes feels that our tests and out code more or less repeat themselves, and maintenance becomes a mindless "change code, change test" activity. I think the value becomes clearer when there are more paths and error handling.

Upvotes: 2

Henk Langeveld
Henk Langeveld

Reputation: 8456

Not only do you verify the interface has been called, you can have multiple tests for different behaviour of the interface. Especially the corner cases -- does your code fail over gracefully when CreateUser returns an error raises an exception?

Upvotes: 2

Related Questions