Skip to main content

Generating smart test templates

Generate smart test templates with all the necessary boilerplate code to reduce the time and effort costs unit and integration testing. Generated smart test templates contain all the necessary function calls, object initializations, asserts, imports, and annotations, leaving you in charge of defining the actual values for testing.

Usage

Open the file you want to analyze and choose any of the following options to trigger the generation of smart test templates:

Code lens

Click the "create test" code lens above any function to generate test templates for that function:

Use code lens.

Context menu

Right-click into a function and select "Symflower: Generate Test Template for Function" from the context menu:

Trigger the context menu.

Keyboard shortcut

Define a keyboard shortcut to trigger Symflower's smart test template generation. Position your cursor within a function and hit your specified keybinding:

Define a keyboard shortcut.

Command palette

Position your cursor into a function. Open the command palette of your editor and start typing "Symflower". Select the entry "Symflower: Generate Test Template for Function" (or use "Symflower: Generate Test Templates for File" if you want to generate test templates for the entire file):

Use the command palette.

Symflower generates a new test method either by extending an already existing test file or by creating a new one. The chosen file location is determined using the test path defined in your build configuration. If none can be found, the test file is placed in the same directory as your production code.

Details

Symflower currently supports the build systems Maven and Gradle for automatic test path detection. The plugin supports the generation of JUnit 4 and JUnit 5 test cases. Have Symflower automatically detect the framework you are using, or select the framework manually in Symflower's configuration options.

Tutorial

This tutorial takes the example of a simple implementation of a class that stores triangles. Besides storing the length of each side of the triangle, the application should also categorize the triangle in question:

  • Equilateral: all sides are the same length
  • Isosceles: two sides have the same length
  • Scalene: none of the sides are equal in length

Step 1: Tutorial example

For this geometric triangle class, we'll need three member variables for the three triangle sides, a constructor, and the available triangle types. Let's also a possible implementation of the getType function.

Copy the following code into a file called Triangle.java:

public class Triangle {
private int sideA;
private int sideB;
private int sideC;

public Triangle(int sideA, int sideB, int sideC) {
this.sideA = sideA;
this.sideB = sideB;
this.sideC = sideC;
}

public TriangleType getType() {
// Each side needs to be greater than 0
if (sideA <= 0 || sideB <= 0 || sideC <= 0) {
return TriangleType.invalid;
}

// Two sides need to be bigger than the third
if(this.sideA + this.sideB < sideC || this.sideB + this.sideC < this.sideA || this.sideA + this.sideC < this.sideB){
return TriangleType.invalid;
}

if (this.sideA == this.sideB && this.sideB == this.sideC) {
return TriangleType.equilateral;
}

if (this.sideA == this.sideB || this.sideB == this.sideC || this.sideA == this.sideC) {
return TriangleType.isosceles;
}

return TriangleType.scalene;
}
}

enum TriangleType {
equilateral,
isosceles,
scalene,
invalid
}

Step 2: Generate test template

Use any of the usage options above to generate smart test templates. A new test file TriangleTest.java will be created with the following contents:

import org.junit.*;
import static org.junit.Assert.*;

public class TriangleTest {
@Test
public void Triangle() {
int sideA = 123;
int sideB = 123;
int sideC = 123;
Triangle expected = new Triangle(123, 123, 123);
Triangle actual = new Triangle(sideA, sideB, sideC);

assertEquals(expected, actual);
}
}

Step 3: Name test case and update values

Next up, choose an appropriate name for your test case and update the test values as you see fit. Your cursor will be automatically moved to the right location to assign a new name to your test case. Any subsequently generated smart test templates will be automatically added to the same test file. Add the desired tests to your test file,

Name test cases and add values.

Scenarios

Generate imports

Upon test template generation, all the required imports will be automatically added to the generated test file.

The following example code relies on the class Triangle which resides in another package.

package com.symflower.example;

import com.symflower.shapes.Triangle;
import com.symflower.shapes.TriangleType;

public class Utils {
Triangle initTriangle() {...}
...
}

In the generated test template file below, the necessary import for Triangle and the required dependencies for unit testing with JUnit 5 have been added automatically:

package com.symflower.example;

import com.symflower.shapes.Triangle;
import com.symflower.shapes.TriangleType;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;

public class UtilsTest {
...
}

Default template values

Symflower's smart test templates initialize variables with a predefined value. The following table lists the default value used per type:

TypeTemplate Value
byte123
short123
int123
long123L
float123.4F
double123.4D
String"abc"
char'a'
booleantrue

For complex data types, Symflower's smart test templates aim to choose the simplest constructor for initialization. For enums, the template value is set to the first encountered enumeration constant.

Let's take a look at a concrete example to see how variables are typically initialized with Symflower's smart test templates. The method under test initTriangle receives a TriangleType and returns an initialized Triangle:

package com.symflower.example;

import com.symflower.shapes.Triangle;
import com.symflower.shapes.TriangleType;

public class Utils {
static Triangle initTriangle(TriangleType t) {...}
}

The generated test template then looks as follows:

package com.symflower.example;

import com.symflower.shapes.Triangle;
import com.symflower.shapes.TriangleType;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;

public class UtilsTest {
@Test
public void initTriangle() {
TriangleType t = TriangleType.equilateral;
Triangle expected = new Triangle(123, 123, 123);
Triangle actual = Utils.initTriangle(t);

assertEquals(expected, actual);
}
}

Note that the required enum t is initialized with the enum value TriangleType.equilateral and the expected Triangle by calling the only provided constructor new Triangle(123, 123, 123).

Generate assertions

The generated assertions depend on the recommended assertion function per type. Most often, assertEquals is used to compare the results of a function. In the case of a returned array type, the function assertArrayEquals is used instead:

package com.symflower.example;

import com.symflower.shapes.Triangle;
import com.symflower.shapes.TriangleType;

public class Utils {
...
static Triangle[] getTriangles(){
...
}
}

Here's what the generated test looks like:

package com.symflower.example;

import com.symflower.shapes.Triangle;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;

public class UtilsTest {
@Test
public void getTriangles() {
Triangle[] expected = { new Triangle(123, 123, 123), new Triangle(123, 123, 123), new Triangle(123, 123, 123) };
Triangle[] actual = Utils.getTriangles();

assertArrayEquals(expected, actual);
}
}
Application Frameworks

Symflower supports Spring Boot offering more boilerplate code specific to Spring Boot tests.