Constructing a Test
If you practice Test-Driven Development (TDD) or Behavior-Driven Development (BDD), you may be familiar with the Arrange, Act, Assert pattern.
The idea is that a single test is organized into three logical sections:
-
Arrange: Do any setup necessary to get the object under test into the correct state.
-
Act: Perform some action on the object under test
-
Assert: Check that the desired results occurred
Here’s an example from the Minitest codebase (comments added by me):
Lately, I’ve noticed a pattern in my test writing that I find interesting. I often write a test like the one above in the following way:
- I ask myself, “What am I testing? What action or behavior do I want to exercise?” This leads me to write the Act part of the test.
- I ask myself, “What should be true after the action?”. This leads me to write the Assert part of the test.
Sometimes, I’ll reverse these two steps and write the Assert part first, then the Act part.
- I then work on the Arrange part of the test. I’ll look at the
Act and Assert parts for anything that is unknown. In the example
above, my internal dialogue runs something like this: “OK, I’m sending
:run
and:passed?
totest
, wheretest
is …”, and then I flesh out whattest
is:
- I repeat the process recursively until I have everything I need to run
the test. So, now I say, “… where
test_class
is … and wheretest_omg
is …”:
- Finally, I ask myself if there is any other setup or peripheral
information that needs to be included. In this case, the test name
includes something about
teardown_good
, and nothing in my test says anything about that. That leads me to add the final line:
I find that this thought process is working fairly well for me, and one of the keys seems to be the magic word I highlighted above: where. When I write the Act and Assert parts of the test, I feel like I’m writing nice, clear, communicative code. I then successively elaborate it by defining my terms.
The odd part is that I’m elaborating up in the code, adding the elaborations above the code that uses them. This is pretty normal in many programming languages: you need to define things before you use them.
But when I started using the word where, it reminded me about functional languages, such as Haskell. I haven’t done much with functional languages, but here’s a quick example from a Haskell tutorial:
Notice how we can write the code we want first, then elaborate it
below using a where
clause.
Interesting.