Icon for gfsd IntelliJ IDEA

Best practices for Java and Go unit tests: naming conventions and structure

Best practices for naming and storing test files with Java and Go, and configuring test files in Symflower.

When applying unit testing on your software, you’ll likely end up with lots of unit tests. In order to stay organized so that your test package structure is transparent and easy for other collaborators to work with, you’ll need to establish a Java (or Go) test naming convention for your unit tests, and you’ll also need to define a Java (or Go) test directory where you’ll be storing those tests. A logical test package structure as well as sound unit test naming conventions make navigation easier for others working with your code.

The test naming convention, as well as the test folder structure you use depends on the programming language, tools, settings and options used for a project. In this article we will take a look at some key best practices for defining Go and Java unit test naming conventions and test folder structures. We will also cover how Symflower can be configured to change your project’s conventions of naming and locating unit test files.

Try Symflower in your IDE to generate unit test templates & test suites

Go best practices for naming and storing unit test files

Go’s testing facilities are directly built into the language. In order for the go test command to recognize a file that contains tests, the file suffix _test.go has to be used. By convention a file named src/mypackage/plain.go has the associated test file src/mypackage/plain_test.go. However, as this is just a convention, Go neither constrains you to place the test file in the same directory, nor to use the same name as the implementation that is tested.

In order to differentiate between unit, integration, and acceptance tests, build tags can be used. Let’s assume we want to test a Ping method on the system level. This could be achieved by adding a file ping_test.go next to ping.go that adds a build tag as follows:

//go:build system

package ui

import (
  "testing"

  "github.com/stretchr/testify/assert"
)

func TestPing(t *testing.T) {
  client := NewClient("https://symflower.com/")
  assert.NoError(t, client.Ping())
}

This test can then be executed by issuing the command go test -tags system mypackage.

You will notice that this command executes the “TestPing” test function, but also every other test function in this package. Why is not only the test file with the “system” tag executed? The reason is that Go also includes files without any build tags.

There are two solutions to this common problem in Go projects:

  • a.) Give all test files at least one build tag, e.g. “unit” for unit tests. However, adding such a requirement opens the problem that one can forget to add the tag for the usual testing workflow.
  • b.) Use dedicated build tags as well as directories “test-integration” and “test-system” per component for integration and system tests, but let unit tests use Go’s regular conventions. This solution makes it possible to explicitly execute only certain types of tests and reduces the maintenance time for regular unit testing.

Java best practices for unit test naming conventions and test folder structure

The Java unit test naming convention for test classes is to prefix the name of the production class with Test. Typically Java test files are kept in separate test directories mirroring the package hierarchy of the implementation. Let’s assume a class Plain is located in the package com.example with the following file location: src/main/java/com/example/Plain.java. The associated Java test file is also in a package called “com.example”, but will be located under: src/test/java/com/example/PlainTest.java.

As for the Java test folder structure: since the exact location for test files is typically not the same over all Java projects, most build tools offer the option to configure the Java test directory that you want to use as the location of test files within a project. Maven, for instance, has as a default location for test files the directory src/test/java, but also offers the possibility to define other locations. Those are the key practices for naming and locating unit test files that you’ll want to stick to for a clear, logical, and transparent structure.

Configuring Symflower’s test file output

Symflower automatically detects the configuration of your project depending on the programming language, tools, settings and options used for the project.

Default test file location

By default, Symflower puts test files next to the file being tested, postfixing the file name with _symflower_test.go in Go and SymflowerTest.java in Java.

For a Java project with the following structure:

└── src
   └── main
       └── java
           └── com
               └── example
                   └── Plain.java

The invocation of symflower leads to the following output, e.g. a PlainSymflowerTest.java test file being placed in the appropriate Java test folder:

├── src
│   └── main
│       └── java
│           └── com
│               └── example
│                   ├── Plain.java
│                   └── PlainSymflowerTest.java

File locations for Maven projects

In case a Maven configuration file pom.xml is present, Symflower does not use its default location for test files but rather uses the specified source directory for tests.

If no test directory is specified within the provided pom.xml, then the default output location src/test/java is used. Resulting in the following project structure:

├── pom.xml
├── src
│   ├── main
│   │   └── java
│   │       └── com
│   │           └── example
│   │               └── Plain.java
│   └── test
│       └── java
│           └── com
│               └── example
│                   └── PlainSymflowerTest.java

In order to instruct Maven to use another source directory for tests than src/test/java, the entry testSourceDirectory needs to be added to the Maven configuration file:

<project>
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.example</groupId>
   <artifactId>example</artifactId>
   <version>0.0.0</version>
   <build>
       <testSourceDirectory>src/mytests/java</testSourceDirectory>
   </build>
</project>

With this configuration in place, running Symflower results in the tests being put into src/mytests/java rather than src/test/java.

Configuring where Symflower should put test files

Besides automatically reusing the existing configuration of your project, Symflower offers two options to define Java test folders e.g. where your test files should be put:

Using a Symflower configuration file

By placing a .symflower/settings.json file in your project root directory with the following content, you can instruct Symflower to put any generated test files into a sub folder bar/baz.

{
  "TestGeneration": {
    "TestsPath": "bar/baz"
  }
}

For our running example this changes the output of Symflower to:

├── bar
│   └── baz
│       └── com
│           └── example
│               └── PlainSymflowerTest.java
├── pom.xml
├── src
│   └── main
│       └── java
│           └── com
│               └── example
│                   └── Plain.java
├── .symflower
│   └── settings.json

Using Symflower’s command option --tests-path

In order to overwrite the test location for a single invocation of Symflower, you can simply use its command option --tests-path:

symflower --tests-path="myTestOutputDirectory"

Resulting in the following directory structure:

├── myTestOutputDirectory
│   └── com
│       └── example
│           └── PlainSymflowerTest.java
├── pom.xml
├── src
│   └── main
│       └── java
│           └── com
│               └── example
│                   └── Plain.java

The option to configure the location of generated tests was a request in our community issue tracker https://github.com/symflower/symflower/issues. We are eager to hear about more features you would like to see in Symflower. So go ahead and make some suggestions. And make sure to register for our newsletter to not miss out on any upcoming features!

| 2022-04-20