Search code examples
javaunit-testingtestingmockingstubbing

Should I mock local method invocation inside a tested method?


My question about the concept of unit testing:

class A {
   public void m1() {
      // code
      m2();
      //code
   }

   public void m2() {
      //some code
   }

}

According to best practices, how should I test the m1 method? Is the unit the class or is the unit the method?

My view - I should test m2 separately and I shouldn't test m1 and m2 integration.

To use my view is hard enough - I should use sophisticated frameworks for testing and use very modern things.

According to my sense of unit testing, tests should be simple! If your code is good, you can test it without sophisticated things. But invoking m2() inside m1() is normal code.

Please clarify my misunderstanding.

update:

mocking example(pseudocode):

//prepare
A testClass = mock(A.class);
when(testClass.m2()).doNothing();
when(testClass.m1()).callRealMethod();
//execute:
testClass.m1();
//verify
check testClass state after method m1 invocation.

This is how I test a mocked class. Is this normal?


Solution

  • First, when you unit-test, test all public methods. In your example m1 and m2 are public, so you'd have tests for both.

    There are several reasons that you might want to stub or mock m2:

    1. If, when you test m1, you encounter any problems because m1 calls m2, stub or mock m2. Some problems you might encounter:

      • m2 might call external services
      • m2 might just be slow
      • it might be difficult to call m1 with parameters that satisfy m2 (your m2 has no parameters, but I'm speaking generally)
    2. Sometimes, when you test a method that calls another method and also test the other method, you find that there is duplication between the tests of the two methods -- some of the tests of the calling method are really testing the called method. Deal with that by stubbing or mocking the called method out of the calling method, testing the calling method just enough to prove that the called method is called, and thoroughly testing the called method.

    3. If you do TDD, you might write m1 before you write m2. You would then stub or mock m2 so that your tests of m1 would pass, and then go on to test and write m2.

    But if you don't have any reason to stub or mock m2, don't. It is common and reasonable for a method to call other methods in ways that don't require stubbing or mocking. The called methods might be short and simple, or the calling method might just be broken up into a bunch of subsidiary methods for readability. That's even true if the called method is in another class (because it's used by more than one calling method); if it is fully tested by tests of methods that call it, and if there is no duplication between tests, there is no need to stub or mock.

    The example above of mocking m2 without running m1 is a perfectly normal thing to want to do and it gets the job done, but it's ugly since Mockito takes over all of a class's methods. You can do it more nicely with a Mockito spy, discussed here: What is the difference between mocking and spying when using Mockito?.

    A a = spy(new A());
    doNothing().when(spy).m2();
    a.m1();
    # check state of a