Implement Table Driven Test in Java

This entry is for GMO Ad Marketing Advent Calendar 2018 (Https://qiita.com/advent-calendar/2018/gmo-am) [12/09] article. This is the first GMO ad marketing entry in the Advent Calendar.

Introduction

In Go language, it is common to write test code in the form of Table Driven Test. The word "Go-ness" is often used among Go engineers. The source code is always required to be written like Go. I think there are some ambiguities in the word Go-ness, but Table Driven Test is one of Go's uniqueness. This time, I will introduce an example of bringing Go-ness to Java.

What is Table Driven Test?

The Table Driven Test is described on the following pages. https://github.com/golang/go/wiki/TableDrivenTests

In Table Driven Test, test cases are represented by a structure, which is looped to execute the test. This time, for the sake of explanation, I created a partial test of pow.go.

package main

import (
	"testing"
	"math"
)

func TestPow(t *testing.T) {
	for _, tt := range []struct {
		name     string
		x        float64
		y        float64
		expected float64
	}{
		{"Pow(2,1)", 2, 1, 2},
		{"Pow(2,2)", 2, 2, 4},
		{"Pow(2,3)", 2, 3, 8},
		{"Pow(2,0)", 2, 0, 1},
		{"Pow(2,-1)", 2, -1, 0.5},
		{"Pow(0,2)", 0, 2, 0},
		{"Pow(2,+Inf)", 2, math.Inf(0), math.Inf(0)},
		{"Pow(2,-Inf)", 2, math.Inf(-1), 0},
		{"Pow(+Inf,2)", math.Inf(0), 2, math.Inf(0)},
		{"Pow(-Inf,2)", math.Inf(-1), 2, math.Inf(0)},
	} {
		t.Run(tt.name, func(t *testing.T) {
			result := math.Pow(tt.x, tt.y)
			if result != tt.expected {
				t.Errorf("expected: %v, result: %v", tt.expected, result)
			}
		})
	}
}

In this example, the structure defines the test name, method arguments, and expected value. By writing like this, what each value represents It also makes it easier to see what you are testing.

Table Driven Test implemented in Java

Use the features added in JUnit 5 to implement Table Driven Test in Java.

This time I created a pow test for the Math class. The example uses lombok to keep the source code simple.

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.experimental.Accessors;
import org.junit.jupiter.api.*;

import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.assertEquals;

class MathTest {

    @Nested
    static class Pow {

        @AllArgsConstructor
        @Getter
        @Accessors(fluent = true)
        static class TestCase {
            private String name;
            private double x;
            private double y;
            private double expected;
        }

        @TestFactory
        Stream<DynamicNode> testPow() {
            return Stream.of(
                    new TestCase("pow(2,1)", 2, 1, 2),
                    new TestCase("pow(2,2)", 2, 2, 4),
                    new TestCase("pow(2,3)", 2, 3, 8),
                    new TestCase("pow(2,0)", 2, 0, 1),
                    new TestCase("pow(2,-1)", 2, -1, 0.5),
                    new TestCase("pow(2,+Inf)", 2, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY),
                    new TestCase("pow(2,-Inf)", 2, Double.NEGATIVE_INFINITY, 0),
                    new TestCase("pow(+Inf,2)", Double.POSITIVE_INFINITY, 2, Double.POSITIVE_INFINITY),
                    new TestCase("pow(-Inf,2)", Double.NEGATIVE_INFINITY, 2, Double.POSITIVE_INFINITY)
            ).map(testCase -> DynamicTest.dynamicTest(
                    testCase.name(),
                    () -> {
                        double result = Math.pow(testCase.x(), testCase.y());
                        assertEquals(testCase.expected(), result);
                    })
            );
        }
    }

}

In JUnit 5, you can define an inner class with @ Nested to nest your tests. In this example, the test is defined by an inner class called Pow.

In Pow, we define an inner class called TestCase. The TestCase fields define the test name, the arguments to pass to the method, and the expected result. The value used in each test case is passed in the TestCase constructor.

If you use a class like Tuple here, It's hard to tell what each value you pass to Tuple represents, By having the test case in an inner class like the example You can associate a value with a named field.

We use JUnit 5's new Dynamic Tests (https://junit.org/junit5/docs/current/user-guide/#writing-tests-dynamic-tests) to run the tests. @testfactoryBy attaching to a method that returns a dynamic node stream, collection, etc. You can run the test dynamically.

Summary

By combining the features of JUnit 5 with the goodness of Go language, I was able to write a highly readable test in Java. It would be great if we could continue to incorporate the good points of other languages, such as Go-ness, into Java.

GMO Advent Calendar 2018 that lasts until Christmas Stay tuned for more posts!

■ Techblog by engineers is now available! https://techblog.gmo-ap.jp/ ■ Wantedly page-Blogs and job offers are now available! ~ https://www.wantedly.com/projects/199431 ■ Engineer recruitment page-Click here for information on benefits and various systems- https://www.gmo-ap.jp/engineer/ ■ Recruiting engineer student internships! -Experience the development site with a paid intern- https://hrmos.co/pages/gmo-ap/jobs/0000027

Recommended Posts

Implement Table Driven Test in Java
Java programmer touched Go language (Implement Java inheritance in Go language)
Facade pattern in Java
Singleton pattern in Java
Flyweight pattern in Java
Observer pattern in Java
Linux permissions in Java
Use DataFrame in Java
Iterator pattern in Java
Implement recommendations in Python
Implement XENO in python
Decorator pattern in Java
Table definition in SQLAlchemy
Implement sum in Python
Prototype pattern in Java
Implement Traceroute in Python 3
Proxy pattern in Java
Tutorial for doing Test Driven Development (TDD) in Flask-2 Decorators
I tried programming the chi-square test in Python and Java.
Tutorial for doing Test Driven Development (TDD) in Flask ―― 1 Test Client
Implement LSTM AutoEncoder in Keras
Implement follow functionality in Django
Algorithm in Python (primality test)
Rename table columns in Django3
Implement timer function in pygame
Implement Style Transfer in Pytorch
Implement recursive closures in Go
Output table structure in Django
Implement naive bayes in Python 3.3
Implement UnionFind (equivalent) in 10 lines
Implement ancient ciphers in python
Implement Redis Mutex in Python
Implement extension field in Python
Implement fast RPC in Python
Template Method pattern in Java
Implement method chain in Python
Implement Dijkstra's Algorithm in python
Implement Slack chatbot in Python
If you write go table driven test in python, it may be better to use subTest
Implement Gaussian process in Pyro
Set python test in jenkins
How to implement Java code in the background of RedHat (LinuxONE)