Skip to content

Instantly share code, notes, and snippets.

@Integralist
Last active March 5, 2025 21:19
Show Gist options
  • Save Integralist/7944948 to your computer and use it in GitHub Desktop.
Save Integralist/7944948 to your computer and use it in GitHub Desktop.
Sandi Metz advice for writing tests

Rules for good testing

Look at the following image...

...it shows an object being tested.

You can't see inside the object. All you can do is send it messages. This is an important point to make because we should be "testing the interface, and NOT the implementation" - doing so will allow us to change the implementation without causing our tests to break.

Messages can go 'into' an object and can be sent 'out' from an object (as you can see from the image above, there are messages going in as well as messages going out). That's fine, that's how objects communicate.

Now there are two types of messages: 'query' and 'command'...

Queries

Queries are messages that "return something" and "change nothing".

In programming terms they are "getters" and not "setters".

Commands

Commands are messages that "return nothing" and "change something".

In programming terms they are "setters" and not "getters".

What to test?

  • Test incoming query messages by making assertions about what they send back
  • Test incoming command messages by making assertions about direct public side effects

What NOT to test?

  • Messages that are sent from within the object itself (e.g. private methods).
  • Outgoing query messages (as they have no public side effects)
  • Outgoing command messages (use mocks and set expectations on behaviour to ensure rest of your code pass without error)
@alphaCTzo7G
Copy link

@Integralist,

How is this different from black-box testing and glass-box testing?

The description of testing philosophy above seems to be similar to black-box testing, but you typically need a combination of black-box testing and glass-box testing because black-box testing doesnt catch all the potential flaws in the program. Without knowing how the function is implemented, you maynot create test-cases which will catch the corner cases.

@aesyondu
Copy link

A thousand words, for reference:

unit-testing-matrix

@randome
Copy link

randome commented Jul 6, 2022

@dgmstuart
Copy link

I never quite understood the outgoing query command part:

Note: there is no point in testing outgoing messages because they should be tested as incoming messages on another object

  1. I guess it assumes that when testing that query on the recipient, you're sending the same kind of data that you're actually sending from the sender.
    • That's some coupling which could get out of sync?
    • I guess that's prevented by integration tests? (ie. tests that assert that all your code is correctly integrated together)
  2. When the query is to an external API, there's no corresponding incoming message spec.
    • Is this an exception to the rule where we actually should assert that the outgoing query is made as expected? Because otherwise our specs could be green even if we're eg. not actually sending that external query?
    • ...or maybe the mental trick is that an external API call is considered a public side-effect, even if it's just a query?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment