开发者

unit test best practice for method with mocks in Mockito

开发者 https://www.devze.com 2023-02-27 09:15 出处:网络
Lets say we have method to test in class A that calls method from class B. To test it we created mock for B and then verify if it was called. Is verify(...) enough for unit test or I need assert actua

Lets say we have method to test in class A that calls method from class B. To test it we created mock for B and then verify if it was called. Is verify(...) enough for unit test or I need assert actual result of tested method? Below is simplified example to clarify my concern:

public class StringWriterATest {
    StringWriterB b = mock(StringWriterB.class);

    @Test
    public void stringWriterATest() {
        StringBuffer sb = new StringBuffer();
        StringWriterA a = new StringWriterA();
        a.stringWriterB=b;

        a.append(sb);

        ArgumentCaptor<StringBuffer> argument = ArgumentCaptor.forClass(StringBuffer.class);
        verify(b).append(argument.capture());

        assertEquals("StringWriterA", ((StringBuffer)argument.getValue()).toS开发者_开发知识库tring());

        //do we really need this or above is enough for proper unit test of method a.append(sb); 
        //assertEquals("StringWriterA_StringWriterB", sb);
    }
}


public class StringWriterA {
    public StringWriterB stringWriterB;

    public void append(StringBuffer sb) {
        sb.append("StringWriterA");
        stringWriterB.append(sb);
    }
}

class StringWriterB {
    public void append(StringBuffer sb) {
        sb.append("StringWriterB");
    }
}

Regards, Max


There is never a need to mock a return value and verify an object at the same time.

Consider this:

StringWriterA is the class under test. Therefore you'll definitely want to use assertions to verify the behavior of this class. In order to do this, you mock out a dependency, StringWriterB.

You do not want to test StringWriterB in your test of StringWriterA, therefore any assertions of StringWriterB interactions in your test are in the wrong place.

You must assume that StringWriterB is behaving as expected. You either want to verify that StringWriterA called StringWriterB correctly (using verify()) or you want to mock its expected behavior and mock the return values.

If you mock, then the verify is implicit since the mocked return value will not be returned if the method is not called.

In your case, StringWriterA.append() does not return any value, so only a verify is even possible. That StringWriterB.append() also works should have a similar verify test in a stringWriterBTest of its own.

Note: It's nice to be explicit with tests. Since test methods are never called outside of a framework, there is never a need to type them out, so you can have much longer method names than in production code methods. A nice convention is:

<underTest>Should<Expected>[When]<Condition>()

i.e.

stringWriterAShouldAppendConstantAndDelegateToStringWriterB() stringWriterAShouldThrowNullPointerExceptionWhenNullArgument()

When you have test failures in your build (continuous integration), then you don't have to hunt down what went wrong, the method name appears right by the failure and you can read it to know exactly what behavior must be fixed.


In your example, StringWriterB stores no state and the append method could easily be static. In that case then the call is purely a side effect and does not need to be tested.

However, I suspect your real code is much more complex. If there is a of another object accessing StringWriterB then you maye want to mock it out in case there are unexpected calls to it. You also may want to add the verify of B if you expect it to be expanded in the future -- possibly storing state from the append call and having accessors.

One thing to consider is what the purpose of the call to StringWriterA.append() is. If it's job is to append the string StringWriterAStringWriterB then that is what you should be testing and a mock is not necessary. How StringWriterA accomplishes that task is immaterial. If, however, part of its job is to also call the StringWriterB.append() method then a mock may will be necessary unless you want to test StringWriterB in A's test.

My rule of thumb WRT mocks is to use real objects until the wiring for the objects I'm not directly testing gets too hairy or too brittle. If I feel like a good portion of my tests are in actuality testing other objects then mocks would be a good idea.


First of all try to show somebody the test you wrote. It is hard to read in my opinion. You can't really tell from it what behaviour you are testing. A brief intro for you how to make it a bit more readable can be found here How to Write Clean, Testable Code . Using argument captors is also a smell. Some examples how to avoid it can be found on this tdd blog.

To answer you question, verify is used to verify interactions between classes. It is used to drive the design of your code. The result (if needed) should be specified by a when or given at the beginning of your test.

Further information how to drive your design with mocks (when, given, verify, ...) and how mocks are different to stubs can be found here: Mocks are not stubs. That example uses JMock not Mockito for defining mocks, but it is very similar (it is about the concepts, not the details of implementation and libraries you use).

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号