简体   繁体   中英

Unit test for method that calls multiple other methods using Mockito

Perhaps I have completely fallen short in my search, but I cannot locate any documentation or discussions related to how to write a unit test for a Java class/method that in turn calls other non-private methods. Seemingly, Mockito takes the position that there is perhaps something wrong with the design (not truly OO) if a spy has to be used in order to test a method where mocking internal method calls is necessary. I'm not certain this is always true. But using a spy seems to be the only way to accomplish this. For example, why could you not have a "wrapper" style method that in turn relies on other methods for primitive functionality but additionally provides functionality, error handling, logging, or different branches dependent on results of the other methods, etc.?

So my question is two-fold:

  1. Is it poorly designed and implemented code to have a method that internally calls other methods?
  2. What is the best practice and/or approach in writing a unit test for such a method (assuming it is itself a good idea) if one has chosen Mockito as their mocking framework?

This might be a difficult request, but I would prefer for those who decide to answer to not merely re-publish the Mockito verbiage and/or stance on spies as I already am aware of that approach and ideology. Also, I've used Powermockito as well. To me, the issue here is that Mockito developed this framework where additional workarounds had to be created to support this need. So I suppose the question I am wanting an answer to is if spies are "bad", and Powermockito were not available, how is one supposed to unit test a method that calls other non-private methods?

Is it poorly designed and implemented code to have a method that internally calls other methods?

Not really. But I'd say that, in this situation, the method that calls the others should be tested as if the others where not already tested separately. That is, it protects you from situations where your public methods stops calling the other ones without you noticing it.

Yes, it makes for (sometimes) a lot of test code. I believe that this is the point: the pain in writing the tests is a good clue that you might want to consider extracting those sub-methods into a separate class.

If I can live with those tests, then I consider that the sub-methods are not to be extracted yet.

What is the best practice and/or approach in writing a unit test for such a method (assuming it is itself a good idea) if one has chosen Mockito as their mocking framework?

I'd do something like that:

public class Blah {
    public int publicMethod() {
        return innerMethod();
    }

    int innerMethod() {
        return 0;
    }
}


public class BlahTest {
    @Test
    public void blah() throws Exception {
        Blah spy = spy(new Blah());
        doReturn(1).when(spy).innerMethod();

        assertThat(spy.publicMethod()).isEqualTo(1);
    }
}

If you really need (or want) to avoid calling the lower-level methods again, you can stub them out instead of mocking them. For example, if method A calls B and C, you can do this:

MyClass classUnderTest = new MyClass() {
    @Override
    public boolean B() {return true;}

    @Override
    public int C() {return 0;}
};
doOtherCommonSetUp(classUnderTest);
String result = classUnderTest.A("whatever");
assertEquals("whatIWant", result);

I've used this quite a quite a bit with legacy code where extensive refactoring could easily lead to the software version of shipwright's disease: Isolate something difficult to test into a small method, and then stub that out.

But if the methods being called are fairly innocuous and don't requiring mocking, I just let them be called again without worrying that I am covering every path within them.

To me, this question relates strongly to the concept of cohesion.

My answer would be:

It is ok to have methods (public) that call other methods (private) in a class, in fact very often that is what I think of as good code. There is a caveat to this however in that your class should still be strongly cohesive. To me that means the 'state' of your class should be well defined, and the methods (think behaviours) of your class should be involved in changing your classes state in predictable ways.

Is this the case with what you are trying to test? If not, you may be looking at one class when you should be looking at two (or more).

What are the state variables of the class you're trying to test?

You might find that after considering the answers to these types of questions, your code becomes much easier to test in the way you think it should be.

The real question should be:

What do I really want to test?

And actually the answer should be:

The behaviour of my object in response to outside changes

That is, depending on the way one can interact with your object, you want to test every possible single scenario in a single test. This way, you can make sure that your class reacts according to your expectations depending on the scenario you're providing your test with.

Is it poorly designed and implemented code to have a method that internally calls other methods?

Not really, and really not! These so called private methods that are called from public members are namely helper methods. It is totally correct to have helper methods!

Helper methods are there to help break some more complex behaviours into smaller pieces of reusable code from within the class itself. Only it knows how it should behave and return the state accordingly through the public members of your class.

It is unrare to see a class with helper methods and normally they are necessary to adopt an internal behaviour for which the class shouldn't react from the outside world.

What is the best practice and/or approach in writing a unit test for such a method (assuming it is itself a good idea) if one has chosen Mockito as their mocking framework?

In my humble opinion, you don't test those methods. They get tested when the public members are tested through the state that you expect out of your object upon a public member call. For example, using the MVP pattern, if you want to test user authentication, you shall not test every private methods, since private methods might as well call other public methods from an object on which depend the object under test and so forth. Instead, testing your view:

@TestFixture
public class TestView {
    @Test
    public void test() {
        // arrange
        string expected = "Invalid login or password";
        string login = "SomeLogin";
        string password = "SomePassword";

        // act
        viewUnderTest.Connect(login, password);
        string actual = viewUnderTest.getErrorMessage;

        // assert
        assertEqual(expected, actual);
    }
}

This test method describes the expected behaviour of your view once the, let's say, connectButton is clicked. If the ErrorMessage property doesn't contain the expected value, this means that either your view or presenter doesn't behave as expected. You might check whether the presenter subscribed to your view's Connect event, or if your presenter sets the right error message, etc.

The fact is that you never need to test whatever is going on in your private methods, as you shall adjust and bring corrections on debug, which in turn causes you to test the behaviour of your internal methods simultaneously, but no special test method should be written expressly for those helper method.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM