should I worry about the unexpected?

The level of validation in typical state testing with assertions is different from typical interaction testing with mocks. Allow me to have a closer look at this subtle difference. My conclusion dares to question the important part of mocking philosophy: worrying about the unexpected.

Let me explain by starting from the beginning: a typical interaction based test with mocks (using some pseudo-mocking syntax):

#1

Typical interaction based test

Here is what the test tells me:

- When you read the article
- then the reader.read() should be called
- and NO other method on the reader should be called.

Now, let’s have a look at typical state based test with assertions:

#2

Typical state based test

Which means:

- When you read the article
- then the article should be read.

Have you noticed the subtle difference between #1 and #2?

In state testing, an assertion is focused on a single property and doesn’t validate the surrounding state. When the behavior under test changes some additional, unexpected property – it will not be detected by the test.

In interaction testing, a mock object validates all interactions. When the behavior under test calls some additional, unexpected method on a mock – it will be detected and UnexpectedInteractionError is thrown.

I wonder if interaction testing should be extra defensive and worry about the unexpected?

Many of you say ‘yes’ but then how come you don’t do state based testing along this pattern? I’ll show you how:

#3

State based test can detect the unexpected!

Which means:

- When you read the article
- then the article should be read
- and no other state on the article should change.

Note that the assertion is made on the entire Article object. It effectively detects any unexpected state changes (e.g: if read() method changes some extra property on the article then the test fails). This way a typical state based test becomes extra defensive just like typical test with mocks.

The thing is state based tests are rarely written like that. So the obvious question is how come finding the unexpected is more important in interaction testing than in state testing?

Let’s consider pros & cons. Surely detecting the unexpected seems to add credibility and quality to the test. Sometimes however, it just gets in the way, especially when doing TDD. To explain it clearer let’s get back to the example #3: the state based test with detecting the unexpected enabled. Say I’d like to test-drive a new feature:

#4

Test-driving new feature

I run the tests to find out that newly added test method fails. It’s time to implement the feature:

Adding new feature

I run the test again and the new test passes now but hold on… the previous test method fails! Note that the existing functionality is clearly not broken.

What happened? The previous test detected the unexpected change on the article – setting the date. How can I fix it?

1. I can merge both test methods into one which is probably a good idea in this silly example. However, many times I really want to have small, separate test methods that are focused around behavior. One-assert-per-test people do it all the time.

2. Finally, I can stop worrying about the unexpected and focus on testing what is really important:

public shouldSetReadDateWhenReading() {
article.read();
assertEquals(today(), article.getReadDate());
}

public shouldReadArticle() {
article.read();
assertTrue(article.isRead());
}

Ok, I know the example is silly. But it is only to explain why worrying about unexpected may NOT be such a good friend of TDD or small&focused test methods.

Let’s get back to mocking.

Most mocking frameworks detect the unexpected by default. When new features are test-driven as new test methods, sometimes existing tests start failing due to unexpected interaction errors. What happens next?

1. Junior developers copy-paste expectations from one test to another making the test methods overspecified and less maintainable.

2. Veteran developers modify existing tests and change the expectations to ignore some/all interactions. Most mocking frameworks enables developers to ignore expectations selectively or object-wise. Nevertheless, it is still a bit of a hassle – why should I change existing tests when the existing functionality is clearly not broken? (like in example #4 – functionality not broken but the test fails). The other thing is that explicitly ignoring interactions is also a bit like overspecification. After all, to ignore something I prefer just not to write ANYTHING. In state based tests if I don’t care about something I don’t write an assertion for that. It’s simple and so natural.

To recap: worrying about the unexpected sometimes leaves me with overspecified tests or less comfortable TDD. Now, do I want to trade it for the quality? I’m talking about the quality introduced by extra defensive test?

The thing is I didn’t find a proof that the quality improved when every test worried about the unexpected. That’s why I prefer to write extra defensive tests only when it’s relevant. That’s why I really like state based testing. That’s why I prefer spying to mocking. Finally, that’s why I don’t write verifyNoMoreInteractions() in every Mockito test.

What do you think? Have you ever encountered related problems when test-driving with mocks? Do you find the quality improved when interaction testing worries about the unexpected? Or perhaps should state testing start worrying about the unexpected?

8 Responses to “should I worry about the unexpected?”

  1. Tomek Kaczanowski Says:

    “and other method on the reader should be called”
    you probably meant:
    “and NO other method on the reader should be called”

    cheers
    Tomek Kaczanowski

  2. Hamlet D'Arcy Says:

    Simply put: verifying expectations leads to overspecified and fragile tests. In practice, I’m just as likely to delete and rewrite a test case with failing expectation as I am to update them to work correctly. When I hit someone else’s failing test cases that verify every expectation possible, it simply isn’t worth my time to try to fix them.

    As for the synopsis of “worrying about unexpected may NOT be such a good friend of TDD”… Heck, it isn’t even a good friend of maintainability!

    My opinion: write side effect free code. Pass in stubs if you must, reverting to spies and mocks only when necessary… and please verify only the things you really care about!

  3. Tom De Wolf Says:

    I share the thought that we should not write verifyNoInteractions everywhere on every mock. But I do see an important difference between state testing and interaction testing.

    Consider a class UnitUnderTest which has state and interacts with AnotherUnit.

    - state testing: we only test state which is accessible in the interface of UnitUnderTest. Internal state, never made public is not to be tested. Hence, state is closely related to encapsulation. And indeed, we do not have to test that for UnitUnderTest no unexpected changes are made to private state.

    - interaction testing: in this case interacting with AnotherUnit implies you ask to do some actions to that unit. Thus this affects the behaviour of the application + it crosses the encapsulation boundary. Therefore it should be tested AND maybe also tested that no other interactions happen with that unit. This because other interaction trigger extra behaviour which is not wanted according to the test.

    Note, I say ‘maybe’ use verifyNoInteractions because if you consider the difference between ‘asking’ and ‘telling’ (see your other blog entry) then clearly only the telling interactions should be verified and if UnitUnderTest also asks things to AnotherUnit then verifyNoMoreInteractions would imply to also verify the ‘asking’ interactions which we want to avoid.

  4. szczepiq Says:

    What about a situation where an object does not have any ‘private’ state? Still, I would like to test only certain public state without worrying about unexpected changes to other public state.

    >Note, I say ‘maybe’ use verifyNoInteractions because if you consider the difference >between ‘asking’ and ‘telling’

    Very true. If I were about writing mockito again, I would definitely consider making verifyNoMoreInteractions() assert only ‘tell’ interactions. Any ideas how to fix it without serious impact on backwards compatibility? :)

  5. Tom De Wolf Says:

    writing verifyNoMoreInteractions to only assert ‘tell’ interactions is difficult because you cannot assume that every method written is only an ‘ask’ or ‘tell’. Any system includes methods that have a side effect and also return information. Thus a combination of ‘ask’ and ‘tell’ :-) Any void method is only a ‘tell’ method but not any returning method is only an ‘ask’ method. But it should be in the ideal case.

    considering upexpected changes to other public state: you can an should write tests that only test changes to certain public state. However, I think that, for each public state there should be one or more tests that test the methods changing that public state.

    Still, one could have a situation where an unexpected change to other public states than the state under test indicates a bug. Then maybe testing the unexpected IS important. It depends on the case at hand I think.

  6. szczepiq Says:

    >Then maybe testing the unexpected IS important. It depends on the case at hand I think.

    That’s true, maybe testing the unexpected is important. However state testing has been here for years and I haven’t noticed anyone complaining on the fact that junit does not check for unexpected. You can achieve it obviously but it is not “in the framework”.

    The way state testing works just feels right to me. If I want to test the unexpected I will be extra explicit and make my test richer. Mocking is different: testing the unexpected is turned on by default. Many times I have to be extra explicit and ignore irrelevant interactions. It makes my tests noisier, not richer…

  7. Tom De Wolf Says:

    I agree that testing the unexpected is important, but that frameworks should not do this by default. You need the choice because sometimes it is not important.

  8. Anonymous Says:

    > If I were about writing mockito again, I would definitely
    > consider making verifyNoMoreInteractions() assert only ‘tell’
    > interactions. Any ideas how to fix it without serious impact
    > on backwards compatibility?

    How ’bout: verifyAsk, verifyTell, and verifyNoMoreTell?!?

    BTW: Both tests in your example will fail ;)

Leave a Reply