On TDD: Literals versus constants in tests

Erik Sacré
4 min readAug 23, 2018
Photo by Clément H on Unsplash

I like my test methods to be small, self-contained and readable. I notice that developers often DRY up their test code by extracting constants, helper methods and classes, moving common initialization code to a setup method, etc.

I always cringe a bit when I see that. On one hand I understand what the developer is trying to do. I would even expect her to do that with any production code. But not for tests! I believe the readability and the ease of understanding tests are of vital importance. So important that I tolerate, no…, expect some duplication in the interest of clarity.

In this article I will go through five versions of the same test in an attempt to make a point about the usage of literals in test code, as opposed to extracting variables or constants.

Arrange/Act/Assert

In my article I refer to arrange/act/assert. This is the AAA-principle by which tests are written:

  • Arrange: prepare everything that’s required for the test
  • Act: the actual action that we want to test
  • Assert: verify the outcome

Constants or literals?

Recently I went through the Bank Kata, TDD-ing outside-in.

Let’s pick out a test method to use as an example for this discussion.

Version 1

The method seems fairly simple. However there are two things bothering me:

  • Inconsistent use of constant (SystemDate) and literal (100)
  • Declaring the expectedTransaction in the arrange-section

When I wrote the test I thought using any random date would not reveal intent, but using a constant called SystemDate did. Whereas the literal value 100 seemed an evident sample value that does not require further explanation.

But looking at it some days later the code bothered me to the point that I tried changing it to see how that felt.

Version 2

In this version I inlined the expectedTransaction. The assertion, or in this case a mock validation, should be last. The (expected) transaction is a kind of literal in that validation. No need to declare it at the start of the method.

I like this change. The test method is now also reduced to three simple lines, one for each ‘A’ of the Arrange/Act/Assert-principle.

But there is still that inconsistency in the usage of literals.

Version 3

Here I replaced the literal ‘100’ with a constant ‘Amount’. When reading the test one has to understand there is some amount, but the actual amount does not matter, hence a generically named constant.

I find that this is the level often displayed in screen casts where the narrator says ‘I don’t like literals in my code’ and then extracts a constant. Also when Googling one finds multiple blog posts and Stack Overflow answers recommending this approach.

I do not like it, and here is why:

  • Coupling: we are reusing the constants across multiple tests. These tests have no relationship whatsoever but we are coupling them by reusing the same generically named constants
  • Distance: although the values should not matter for the understanding of the tests we are programmed to look at the constants anyway just to see what’s in them, to fully understand the code
  • Those bland abstract names do not tell me more than any simple sample value

Countering the anti-literals movement (well… it’s not really a movement) are at least two well-known and highly respected people:

  • Kent Beck in ‘Test-Driven Development By Example’
  • Jay Fields in ‘Working effectively with unit tests’

Both make the case for using literals.

Use data that makes the tests easy to read and follow… Include expected and actual results in the test itself, and try to make their relationship apparent (Kent Beck)

Version 4

The coupling between tests is removed, and the distance between their declaration and their usage is much smaller. In fact: the method is fully self-contained.

I’m on the fence about this version. I like:

  • The way the constants are named help provide meaning. The usage of ‘the’ and ‘an’ add more context
  • There is no duplication within the test method. Both constants are used twice

I do not like:

  • The test method has grown by 2/3rds
  • Does it really help in understanding the code?

Version 5

And I must admit I’m also on the fence about this version. I like:

  • The fact that the test method returned to these 3 simple lines
  • I believe this value to be just as clear as version 4, i.e. I really do not think the names of the constants added much for this particular test

I do not like:

  • Duplication of literals. In this case there are only 2 values so it is quite manageable. But what if we had more complex data such as an entity with 5 fields?
  • It costs just a bit more effort to see that the verification uses the same values as the arrange/act lines — but for a test of this size it is a non-issue

Conclusion

After much consideration I left the code for this kata at version 5. I would definitely go for version 4 or version 5, not for version 1–3. (I know, I know… easy to claim, but I originally wrote version 1 didn’t I?)

I aim for my tests to be self-contained. I’m fine with having some duplication to make that happen. In my opinion test code should be DAMP (focus on readability), not DRY (focus on orthoganility).

I think using literals is preferred over using constants. But… sometimes explicitly naming the literals reveals the intent more clearly.

--

--

Erik Sacré

Living in that tiny space where technology, user experience and product design meet. http://sacryn.eu