Ready for another whiskey moment with Arlo? This month he’s talking about why acceptance tests should not be separate from unit tests. The blog will tell you how!
If you’d like to chat with Arlo and fellow menders, register for our next virtual Ask Me Anything on August 25 or join our Code by Refactoring slack channel.
Are you tired of stories bouncing back when you get to acceptance testing? Are you tired of “what about [insert unmentioned request here]?” Or, you are already using acceptance testing tool like Cucumber but tests breaks easily, or slow, or you spend a lot of time writing fixtures and adaptors? That’s not even mentioning tests not supporting refactoring.
Unit tests are easy to maintain, fast to run, quick to write, and don’t break. They have great traits! However, they are typically written using developer language and representing all the details. The PO cares deeply about 5% of it, and usually can’t sort through it. This is why acceptance tests were created; the 5%. But they don’t match the design of the code, so they run slowly, break frequently, and require developers to write adaptors.
We’re going to ignore the default solution because it’s cumbersome, error prone, duplicative, and impedes communication. We know that the real (#impossible) solution is making each test both a good unit test and a good acceptance test. Let’s discuss how we can do that.
Scenario
A recent client had a system covered by decent acceptance tests, but they were not good unit tests. They provided reasonable protection. However, they caused a set of a problems while not providing other benefits that a good unit test suite would provide. Here is one example test.

Let’s walk through the series of improvements that we applied.
Minor modifications included to protect client IP.
Extraction | Hide irrelevant information
The test above contained a lot of information that is required to satisfy the computer’s ability to execute the system, but is not relevant to the reader or the purpose of the test.
Lines 5-26 is now represented in lines 6-7, which is what’s left after we extracted the irrelevant information.

Since we need the information for the computer, we extracted it a couple of helper methods.
Extraction | Use custom and fluent assertions
Now the biggest wall of text left in the test is the block of assertions in lines 13-20.
It’s hard to tell what the test is actually attempting to verify. It is sufficient for a computer, but a human reader gets no value from this. We need clear intentions. Talk to your PO to understand the intention of the test! Then you can extract a custom assertion to make your intention clear, as we did on lines 12-14.
The assertions are now so clear that we don’t need a comment introducing the block.

So now we have clear intentions, and, since we simply extracted them, the computer still has it’s definitions to verify … thankfully in a place that doesn’t distract the human reader.
Extraction | Use arbitrary constants
Each of the places boxed in red (below) used to have concrete value. For example, Arbitrary.DateTime() had May 15. While the computer needs to verify against a specific data value, such as May 15, the concrete value of May 15 is actually irrelevant to a human’s understanding. For this test, the only things humans care about is that the sighting had a date (line 6), and that the report’s filter range went from before that date to after that date (lines 12-13).

Rename | Have a clear test subject
The test at it’s current state (above) is now clear about purpose and context, but it’s unclear what part of the code we’re actually trying to test, and what part is just required context. So we identify the object or method we are trying to test. In this case, it is the variable on line 8. We name it testSubject to make it obvious.

Apply Design Pattern | Use builders for starting state
Now our test is looking pretty good. Stepping back a minute though, we have a lot of test helper code that is setting up the context. Test helpers like this indicate a design flaw in our production code. It is hard to construct and working with it requires many extraneous details. Talk to your PO to figure out which construction details are essential customer information, and which ones are noise.
Diving in for a second, the solution we chose for this case is to use the builder pattern. Lines 4-6 call the test helper method that we want to simplify. We extracted a builder from those helper methods and in-lined it back to this test. That created the new lines 5-8.

Rename | Name test for PO discovery
The current test is one detail among thousands. We need to determine if a PO cares about this particular detail, and if so, when. Most tests will be computer level details that the PO will never care about. Hide these in a developer only section.
Lift the others to a namespace and call it spec. Then organize your spec namespace into sub-namespaces by epics, features, and stories that the PO cares about. Do this with your PO!
Summary
This is a great opportunity to get on a similar technical page with your PO. Forward this to them! Even better, do an example test and talk to them about it. Additionally, if you’d like your PO to welcome refactoring, have them register for our June class that addresses the business value of refactoring!
Become an Agile Developer
Master Extreme Programming practices like TDD and refactoring while becoming a Certified Scrum Developer in one of David Bernstein's online classes:
June 20 – 24, 2022 (full-days)— Scrum Developer Certification (SDC)
June 28 – July 1, 2022 (half-days) — Certified Scrum Developer (CSD)
July 13 – 14, 2022 (full-days) — Certified Scrum Developer (CSD)
Deep Roots Events
June 23 | Business Value of Refactoring
Would you like your PO to be on the same refactoring page as you and your team? Have your PO register for our June class that addresses the business value of refactoring. Give them a 50% discount code: “newsletter”.
July 11 - 22 | Refactoring Well with Naming as a Process (NaaP)
Ready to up-level your refactoring skills with Arlo? Join our July workshop! Early bird pricing available through June 30. Limited seating.
Missed CRAFTcon?
Marian, learner designer and Arlo-brain-translator for Deep Roots, refactored a legal contract using Naming as a Process (NaaP). She showed how to refactor for readability in a safe way. Check out the slide deck and other resources!
I love this approach Arlo -- so much more productive than using an extra layer of indirection such as Cucumber.
I also really liked your use of an Arbitrary namespace to make it clear what's inessential gubbins just to make the test compile.
And finally I like your use of images for the code snippets, instead of Substack's terrible code blocks. I'm definitely going to steal that idea 🙂
Awesome and thanks so much for this feedback! Agreed that Substack's code blocks are pretty terrible - glad to inspire :)