Icon for gfsd IntelliJ IDEA

Best practices for test files in Go and Java

Best practices for where to store and name test files with Go and Java, and how to configure them in Symflower.

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

Go best practices for naming and locating unit test files

The Go 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 naming and locating unit test files

The Java naming convention for test classes is to prefix the name of the production class with the suffix Test. Typically test files are kept in separate 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, then the associated test file is also in a package called “com.example”, but will be located under: src/test/java/com/example/PlainTest.java.

As the exact location for test files is typically not the same over all Java projects, most build tools offer the option to configure 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.

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 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:

├── 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 where 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!

Technical | 2022-04-20