1. Overview
JGoTesting is a JUnit-compatible testing framework inspired by Go’s testing package.
In this article, we’ll explore the key features of the JGoTesting framework and implement examples to showcase its capabilities.
2. Maven Dependency
First, let’s add the jgotesting dependency to our pom.xml:
<dependency>
<groupId>org.jgotesting</groupId>
<artifactId>jgotesting</artifactId>
<version>0.12</version>
</dependency>
The latest version of this artifact can be found here.
3. Introduction
JGoTesting allows us to write tests that are compatible with JUnit. For every assertion method JGoTesting provides, there is one in JUnit with the same signature, thus implementing this library is really straightforward.
However, unlike JUnit, when an assertion fails, JGoTesting doesn’t stop the execution of the test. Instead, the failure is recorded as an event and presented to us only when all assertions have been executed.
4. JGoTesting in Action
In this section, we will see examples of how to setup JGoTesting and explore its possibilities.
4.1. Getting Started
In order to write our tests, let’s first import JGoTesting’s assertion methods:
import static org.jgotesting.Assert.*; // same methods as JUnit
import static org.jgotesting.Check.*; // aliases starting with "check"
import static org.jgotesting.Testing.*;
The library requires a mandatory JGoTestRule instance marked with the @Rule annotation. This indicates that all tests in the class will be will be managed by JGoTesting.
Let’s create a class declaring such rule:
public class JGoTestingUnitTest {
@Rule
public final JGoTestRule test = new JGoTestRule();
//...
}
4.2. Writing Tests
JGoTesting provides two set of assertion methods to write our tests. The names of the methods in the first set start with assert and are the ones compatible with JUnit, and the others start with a check.
Both sets of methods behave the same, and the library provides a one-to-one correspondence between them.
Here’s an example to test whether a number is equal to another, using both versions:
@Test
public void whenComparingIntegers_thenEqual() {
int anInt = 10;
assertEquals(anInt, 10);
checkEquals(anInt, 10);
}
The rest of the API is self-explanatory, so we won’t go into further details. For all examples that follow, we are going to focus only on the check version of the methods.
4.3. Failure Events and Messages
When a check fails, JGoTesting records the failure in order for the test case to continue its execution. After the test ends, the failures are reported.
Here’s an example to show what this looks like:
@Test
public void whenComparingStrings_thenMultipleFailingAssertions() {
String aString = "The test string";
String anotherString = "The test String";
checkEquals("Strings are not equal!", aString, equalTo(anotherString));
checkTrue("String is longer than one character", aString.length() == 1);
checkTrue("A failing message", aString.length() == 2);
}
After executing the test, we get the following output:
org.junit.ComparisonFailure: Strings are not equal!
expected:<[the test s]tring> but was:<[The Test S]tring>
// ...
java.lang.AssertionError: String is longer than one character
// ...
java.lang.AssertionError: Strings are not the same
expected the same:<the test string> was not:<The Test String>
Besides passing the failure messages in each method, we can also log them so that they only appear when a test has at least one failure.
Let’s write a test method that puts this into practice:
@Test
public void whenComparingNumbers_thenLoggedMessage() {
log("There was something wrong when comparing numbers");
int anInt = 10;
int anotherInt = 10;
checkEquals(anInt, 10);
checkTrue("First number should be bigger", 10 > anotherInt);
checkSame(anInt, anotherInt);
}
After test execution, we get the following output:
org.jgotesting.events.LogMessage: There was something wrong
when comparing numbers
// ...
java.lang.AssertionError: First number should be bigger
Notice that in addition to logf(), which can format messages as the String.format() method, we can also use the logIf() and logUnless() methods to log messages based on a conditional expression.
4.4. Interrupting Tests
JGoTesting provides several ways to terminate tests cases when they fail to pass a given precondition.
Here is an example of a test that ends prematurely because a required file doesn’t exist:
@Test
public void givenFile_whenDoesnotExists_thenTerminated() throws Exception {
File aFile = new File("a_dummy_file.txt");
terminateIf(aFile.exists(), is(false));
// this doesn't get executed
checkEquals(aFile.getName(), "a_dummy_file.txt");
}
Notice that we can also use the terminate() and terminateUnless() methods to interrupt test execution.
4.5. Chaining
The JGoTestRule class also has a fluent API that we can use to chain checks together.
Let’s look at an example that uses our instance of JGoTestRule to chain together multiple checks on String objects:
@Test
public void whenComparingStrings_thenMultipleAssertions() {
String aString = "This is a string";
String anotherString = "This Is a String";
test.check(aString, equalToIgnoringCase(anotherString))
.check(aString.length() == 16)
.check(aString.startsWith("This"));
}
4.6. Custom Checks
In addition to boolean expressions and Matcher instances, JGoTestRule‘s methods can accept a custom Checker object to do the checking. This is a Single Abstract Method interface which can be implemented using a lambda expression.
Here’s an example that verifies if a String matches a particular regular expression using the aforementioned interface:
@Test
public void givenChecker_whenComparingStrings_thenEqual() throws Exception {
Checker<String> aChecker = s -> s.matches("\\d+");
String aString = "1235";
test.check(aString, aChecker);
}
5. Conclusion
In this quick tutorial, we explored the features JGoTesting provides us for writing tests.
We showcased the JUnit-compatible assert methods as well as their check counterparts. We also saw how the library records and reports failure events, and we wrote a custom Checker using a lambda expression.
As always, complete source code for this article can be found over on Github.