1

I've just read a lot of arguments against using Mockito.reset and can't make much sense of it. Put differently, I agree in general, but there's always a case which looks like using reset is useful or even necessary.

Grossly simplified, my class under test is an encapsulated map like

class Handler {
    private final Map<String, String> map;

    public void handle(RequestResponse rr) {
        if (rr.isPut()) {
            map.put(rr.key(), rr.value());
            rr.response("OK");
        } else {
            rr.response(map.get(rr.key());
        }
    }
}

To test it, I mock RequestResponse and do something like

when(rr.isPut()).thenReturn(true);
when(rr.key()).thenReturn("key");
when(rr.value()).thenReturn("value");
handler.handle(rr);
verify(rr).response("OK");

reset(rr);

when(rr.isPut()).thenReturn(false);
when(rr.key()).thenReturn("key");
handler.handle(rr);
verify(rr).response("value");

reset(rr);

when(rr.isPut()).thenReturn(false);
when(rr.key()).thenReturn("anotherKey");
handler.handle(rr);
verify(rr).response(null);

I can see, I'm testing three things at once:

  • proper response after "put"
  • proper value returned from get with the same key
  • null returned from get with another key

I can imagine reasons to separate the two last tests. However, they both need the first one and I still need a reset in between? Or don't I?

Update

In the meantime the class under test has got a bit more complicated and the need for reset bigger. It's sort of a state machine and the test looks like

  • repeat the following accross different paths
    • try an illegal step and expect an exception
    • try another illegal step and expect an exception
    • do a legal step and verify output

I actually only need two methods, one verifying the exception for an illegal step and the other verifying correct output for a legal step. Executing an illegal step mustn't change the state, so it's safe to mix it in.

Consider this example: If the only valid input sequence was A, B, C (three steps) and the corresponding outputs were a, b, c, then a test looks like

checkException("B")
checkException("C")

checkOutput("A", "a")
checkException("A")
checkException("C")

checkOutput("B", "b")
checkException("A")
checkException("B")

checkOutput("C", "c")
checkException("A")
checkException("B")
checkException("C")

walks through the whole path and verifies everything. It would take many "normal" single-condition-verifying tests to achieve the same, right?

4
  • This seems like a strange use of mocks. Why are you mocking RequestResponse? Also, the first block of code should be either setup or an actual test. Which one is it?
    – Andres F.
    Aug 29 '14 at 20:23
  • @AndresF. I'm mocking RequestResponse since I want it to do exactly what told to. My strange test is a sequence of setup + handle-call + test, repeated three times, i.e., actually three tests in one. Separating them means a lot of code duplication and still I need to put the data in somehow...
    – maaartinus
    Aug 29 '14 at 20:29
  • I'm not very dogmatic, but usual unit testing practice recommends splitting the 3 tests, so that each unit test only has one reason to fail. I think it's confusing whether the first block is setup or test code because there is a verify in there. I would decide what it is; if it's setup after all, I'd put it in a @Before method.
    – Andres F.
    Aug 29 '14 at 20:33
  • (I'm not saying your test is definitely wrong, which is why I'm not posting this as an asnwer. It does look funny to me though)
    – Andres F.
    Aug 29 '14 at 20:35
1

The reason that you are using reset is because you are processing three different RequestResponses through your handler. But reset is actually for when you need to change the responses that an object is making. I think the conceptually correct thing for you to do is create three different RequestResponse objects. That way you don't need to reset anything.

But your test is still awkward. I think its awkward because the interface is awkward. RequestResponse fills in the role of both parameters and return value. One approach would be to write an adapter into a more typical approach, something like:

class Handler {
    private final Map<String, String> map;

    public String handle(boolean isPut, String key, String value) {
        if (isPut) {
            map.put(key, value);
            return "OK";
        } else {
            return map.get(key);
        }
    }
}

class HandlerAdapter {
    final Handler handle;

    public HandlerAdapter(Handler handler) {
         this.handler = handler;
    }

    public void handle(RequestResponse rr) {
       rr.response(handler.handle(rr.isPut(), rr.key(), rr.value()));
    }
}

Your test then becomes:

assertEquals("OK", handler.handle(true, "key", "value"));
assertEquals("value", handler.handle(false, "key"));
assertEquals(null, handler.handle(false, "anotherkey"));

I'd probably split that into two tests, checking for successful return and mismatched key in seperate tests. Of course you'll also want to right tests for HandlerAdapter, but that should be really straightforward since it only passes data into Handler.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.