Matchsticks1

© Image by Margarethe Pfründer-Sonn↗

The easy part: Class names

Start

Your test class’ name should have the name of the implementation class with a “Test” suffix.

Example:

Your implementation class is “PriceCalculator”,

so your unit test class name is “PriceCalculatorTest”.

Continue

When your test class is growing too big, with many test methods, you can separate the aspects of the implementation class. Place the name of the aspect in front of the “Test” suffix.

Example:

Your implementation class is “PriceCalculator”,

so your unit test class names are “PriceCalculatorTest”, “PriceCalculatorVatTest”, “PriceCalculatorDiscountTest”, etc.

Hint: Often the implementation class appeared too big to me some weeks later. Then most of the time, the split test classes show me how to divide the implementation class into different parts.

Integration tests

If your test is not a “unit test” using mocks, but maybe it’s a spring test↗, then prefer the “IT” suffix for integration tests.

Example:

Your implementation class is “PriceCalculator”,

so your integration test class name is “PriceCalculatorIT”.

Why?

If you use this naming schema, then you can easily switch between test classes and implementation classes in IntelliJ IDEA↗ using the shortcut CTRL + SHIFT + T *.

*) Shortcut on macOS may be different

The difficult part: Method names

In my last post, I’ve written about what to do if you have unit tests that are hard to understand. Those tests can cost you a lot of time and money. So:

When you write automated tests, you should not only write them to prove that the implementation is correct. You should write them to document WHY the implementation is correct.

The implementation is correct because it fulfils a business need.

In the early days of JUnit, you had to start a test method’s name with „test“. So if the implementation method was „calcPrice“, then the test method was usually „testCalcPrice“. The test method’s name did not add much documentation. What about: calcPrice_adds_the_VAT_to_the_NET_price()? OK, I used underscores instead of camel case, but I think you got the message:

The test method’s name clearly states the business need.

If the test fails, people will know the reason for the test. The name helps them to understand:

  • Should I change the test?
  • Do I need to revert the last change in the implementation code?
  • Maybe we can refactor things to keep the existing test green and meet the new requirements?

Avoid using the words “correct”/”correctly”, “right”, “proper”/”properly” or similar words, they do not help anybody to better understand the intention of the business logic.

If you do not want to write method names that long, you have some other options:

  • Using groovy and spock framework↗, you can use strings for method names:

      def "pushing an element on the stack"() { 
          // blocks go here 
      }
    
  • Using JUnit 5, you can use the @DisplayName↗ annotation to add more descriptions.

“But I don’t have time for thinking that much about naming!”

Please read the chapter about naming in Robert C. Martin’s book “Clean Code”↗!

“But my implementation is purely technical, no business logic!”

Maybe you are right. If this is true: Isn’t there an open-source solution for your problem already?

But maybe you do not look carefully enough for the business domain in your code. We developers often jump into the solution of the feature without first thinking about the domain. And afterwards, we only talk about features, functions and classes, and we do not talk about the reason for all of that anymore. That’s one reason we have so much accidental complexity in our code. Please read the chapter about “ubiquitous language” in Eric Evan’s book “Domain Driven Design↗ to learn about modelling the domain.

In my experience, there are mostly only three kinds of software:

  • “technical” infrastructure code: Imho, that should better be open sourced
  • domain code: You should prefer to use domain language over technical language as much as you can.
  • glue code: most of this is declarative configuration. For declarative code, you should not use unit tests! You should better test that the requirements you have wanted to achieve are met. This is usually done on the component level. Example: If you have some authentication configuration, you should make sure that an unauthenticated request will not be processed.

Test method order

When you start coding, your first test should be the plain vanilla happy path test:

The standard way that the business usually goes. This happy path test should stay at the top test method of your test class/file. So if a new developer joins the code, he can easily understand the business logic by reading the tests.

The exceptional and error cases should be tested below the happy path test.

Even for exceptional case tests:

Please try to find test names that are close to the terms of the business domain; please try to avoid purely technical terms.

Last hint for today:

When implementing a new feature, I love to split the screen and have the implementation class on the left side and the test class on the right side (or vice-versa). This helps me easily switch between test and implementation while keeping the focus.

My next post is about how to implement a test method.

More about Testing

Any comments or suggestions? Leave an issue or a pull request!