Naming Unit Tests

This entry is for anyone who hasn’t done much TDD in Java yet and plans on making the transition.

If you have ever written or looked at specs written with frameworks like RSpec or Jasmine, you will have noticed the “describe” and “it” keywords that lend the spec a certain level of legibility. These frameworks are considered “BDD” tools because when you write tests a certain way, you effectively specify behavior right in the test names.

While JUnit does not lend itself to this as readily, I believe it can be simulated well enough to bother with. In the spirit of specification by example, I will proceed with an example.

Let’s take a method named isAPalindrome.

If this were Ruby, it would probably end with a question mark but, in any case, the specs would read something like:

describe "isAPalindrome?" do
it "should return true given short palindrome string" do ...
it "should return false given short non-palindrome string" do ...
it "should ignore non-breaking whitespace" do ...
it "should return false given empty string" do ...
it "should throw error given nil" do ...
it "should return false given non-alpha string" do ...
...

This isn’t a complete list of specs but it gives you an idea of the method’s behavior and it reads fairly well.

Here’s what I often see when I look at a JUnit test for a method named isAPalindrome:

@Test
public void testIsAPalindrome() {
...
}

Yep. That’s it.

It’s not descriptive. It doesn’t tell you anything about what isAPalindrome does. In fact it’s not even necessarily clear that isAPalindrome is a method under test.

There’s also redundancy. We know it’s a test — it’s annotated with @Test. JUnit 4 has been around for a long time. There’s no need to include the test- prefix. Of course, then you’re left with a test method name that happens to exactly match the method being tested, and that’s not great.

The solution, then, is to follow a better naming convention in JUnit. There are many that are decent (some are here: https://dzone.com/articles/7-popular-unit-test-naming). There’s a convention that I like to use, and while it’s fairly verbose, it has always served me very well.

${methodName}Should${exhibitBehavior}Given${inputOrCondition}

It starts with the name of the method under test. That is actually key. This lets you look at your code outline in Eclipse or IntelliJ and see, at a glance, what methods are directly covered by tests. It also lets you group tests by method name.

Not only that, but the convention lets you easily see what behaviors are being covered for each method. This means that when it is time to add a new behavior, you know exactly how the test for it should read. Everyone on the team does. And when it is time for a peer review, your peers will know what test to look for.

If you encounter a bug, it will mean either an existing test is incorrect or a test is missing. Naming your tests according to this pattern will make that discovery trivial.

Here’s my JUnit test for the method we looked at earlier (pretend they all start with <code>public void</code>):

@Test
"isAPalindromeShouldReturnTrueGivenShortPalindromeString" () {...}
@Test
"isAPalindromeShouldReturnFalseGivenShortNonpalindromeString" () {...}
@Test
"isAPalindromeShouldIgnoreNonbreakingWhitespace" () {...}
@Test
"isAPalindromeShouldReturnFalseGivenEmptyString" () {...}
@Test
"isAPalindromeShouldThrowExceptionGivenNull" () {...}
@Test
"isAPalindromeShouldReturnFalseGivenNonalphaString" () {...}
...

And there you have it. It’s not as easy to read as RSpec but it’s not RSpec. Adding underscores can help readability in some cases and not others. I would rather not worry about trying to push a convention about when those should and shouldn’t be used; “Should” and “Given” are good enough delimiters for me at the moment. And if you want to use “When” instead of “Given” some or all of the time, that also works.

The important thing is to have the method name, then a portion of the spec for that method. That’s how you end up with readable, useful unit test names that make life easier in the long run. A test report can tell you precisely what’s being tested (and what’s not), which is really great. It then makes it easier to identify what integration tests to write, what end-to-end tests to write, and so forth. It makes duplication of effort less likely.

If you have another good naming convention that provides the same amount of value (or more), I definitely want to know about it.