Brock
McElroy

What To Unit Test

30 May, 2021

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.