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):

An Example Test
def test_passed_eh_teardown_good
# Arrange
test_class = Class.new Minitest::Test do
def teardown; assert true; end
def test_omg; assert true; end
end
test = test_class.new :test_omg
# Act
test.run
# Assert
assert test.passed?
end

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.
What Am I Testing?
def test_passed_eh_teardown_good
# Act
test.run
end
  • I ask myself, “What should be true after the action?”. This leads me to write the Assert part of the test.
What Should Be True?
def test_passed_eh_teardown_good
# Act
test.run
# Assert
assert test.passed?
end

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? to test, where test is …”, and then I flesh out what test is:
What is test?
def test_passed_eh_teardown_good
# Arrange
test = test_class.new :test_omg
# Act
test.run
# Assert
assert test.passed?
end
  • I repeat the process recursively until I have everything I need to run the test. So, now I say, “… where test_class is … and where test_omg is …”:
What are test_class and test_omg?
def test_passed_eh_teardown_good
# Arrange
test_class = Class.new Minitest::Test do
def test_omg; assert true; end
end
test = test_class.new :test_omg
# Act
test.run
# Assert
assert test.passed?
end
  • 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:
The Final Test
def test_passed_eh_teardown_good
# Arrange
test_class = Class.new Minitest::Test do
def teardown; assert true; end
def test_omg; assert true; end
end
test = test_class.new :test_omg
# Act
test.run
# Assert
assert test.passed?
end

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:

Haskell Functions
initials :: String -> String -> String
initials firstname lastname = [f] ++ ". " ++ [l] ++ "."
where (f:_) = firstname
(l:_) = lastname

Notice how we can write the code we want first, then elaborate it below using a where clause.

Interesting.