Testing And Benchmarking in Mercury

Here’s the translation of the Go testing and benchmarking example to Java, formatted for Hugo:

Unit testing is an important part of writing principled Java programs. The JUnit framework provides the tools we need to write unit tests, and IDEs or build tools like Maven or Gradle can run these tests.

For the sake of demonstration, this code is in a single file, but it could be split into separate files. Testing code typically lives in a separate directory but in the same package as the code it tests.

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import static org.junit.jupiter.api.Assertions.*;

public class IntUtilsTest {

    // We'll be testing this simple implementation of an integer minimum.
    public static int intMin(int a, int b) {
        return a < b ? a : b;
    }

    // A test is created by writing a method with the @Test annotation.
    @Test
    public void testIntMinBasic() {
        int ans = intMin(2, -2);
        // assertEquals will report test failures if the condition is not met.
        assertEquals(-2, ans, "IntMin(2, -2) should be -2");
    }

    // Writing tests can be repetitive, so it's idiomatic to use
    // parameterized tests, where test inputs and expected outputs
    // are provided as parameters to a single test method.
    @ParameterizedTest
    @CsvSource({
        "0, 1, 0",
        "1, 0, 0",
        "2, -2, -2",
        "0, -1, -1",
        "-1, 0, -1"
    })
    public void testIntMinParameterized(int a, int b, int expected) {
        int ans = intMin(a, b);
        assertEquals(expected, ans, 
            String.format("IntMin(%d, %d) should be %d", a, b, expected));
    }
}

To run the tests, you would typically use your IDE’s test runner or a build tool like Maven or Gradle. Here’s an example of what the output might look like:

JUnit Jupiter > IntUtilsTest > testIntMinBasic() PASSED
JUnit Jupiter > IntUtilsTest > testIntMinParameterized(int, int, int) PASSED
    ✓ [1] 0, 1, 0
    ✓ [2] 1, 0, 0
    ✓ [3] 2, -2, -2
    ✓ [4] 0, -1, -1
    ✓ [5] -1, 0, -1

Test run finished after 123 ms
[         3 containers found      ]
[         0 containers skipped    ]
[         3 containers started    ]
[         0 containers aborted    ]
[         3 containers successful ]
[         0 containers failed     ]
[         6 tests found           ]
[         0 tests skipped         ]
[         6 tests started         ]
[         0 tests aborted         ]
[         6 tests successful      ]
[         0 tests failed          ]

For benchmarking in Java, we typically use frameworks like JMH (Java Microbenchmark Harness). Here’s a simple example of how you might set up a benchmark:

import org.openjdk.jmh.annotations.*;
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
public class IntMinBenchmark {

    @Benchmark
    public void benchmarkIntMin() {
        IntUtilsTest.intMin(1, 2);
    }

    // To run the benchmark, you would typically set up a separate main method
    // or use your IDE's JMH plugin.
}

To run benchmarks, you would typically use JMH’s command-line tools or an IDE plugin. The output might look something like this:

Benchmark                   Mode  Cnt   Score   Error  Units
IntMinBenchmark.benchmarkIntMin  avgt    5   2.541 ± 0.046  ns/op

This indicates that the intMin method takes about 2.541 nanoseconds on average to execute.

Remember that precise benchmarking in Java can be complex due to factors like JIT compilation and garbage collection. Always interpret benchmark results carefully and consider the specific environment and conditions under which they were run.