What To Unit Test
It's worth watching this talk by Sandi Metz. It was given at a Ruby conference, but the concepts are language independent. Really, watch it; it will be more effective than just trying to read the below.
Query | Commmand | |
---|---|---|
Incoming | Assert Return Value | Assert Side Effect |
Self to Self | Don't Test | Don't Test |
Outgoing | Don't Test | Assert Message Sent |
To Summarize
It is important to be selective with what we test! A set of unit tests should a single, contained object, and nothing else. If our tests are too broad, then we begin testing other objects or implementation details, and our tests become fragile and interdependent.
Don't Test Implementation
We should only test the messages sent to and from the object. We do not want to test how the object is implemented, because it really does not matter. As long as the object behaves correctly, our tests should pass.
Do Test Messages
We are testing two types of messages:
- Query: Returns a value
- Command: Effects/changes something
- It is possible to be both, e.g.
Array.pop()
returns a value and removes it from the array.
It matters where these messages come from in relation to the object:
- Incoming: Directed at the object from the external world
- Outgoing: Originating from the object
Which Messages To Test
Again, the chart:
Query | Commmand | |
---|---|---|
Incoming | Assert Return Value | Assert Side Effect |
Self to Self | Don't Test | Don't Test |
Outgoing | Don't Test | Assert Message Sent |
Notice how one object's incoming message must be another object's outgoing message. This has some implications for which messages we should test.
It doesn't make sense to test an object's outgoing query, because we are already testing the same message as an incoming query in the tests for the object that actually generates the response.
We do want to test that outgoing commands are sent, but we don't need to test what their effects are. This is because we will test any incoming command's effects in the tests for the affected object.