Specnaz 1.5 released!
When I first released Specnaz, my testing library for Java, Kotlin and Groovy, back in 2016, it leveraged JUnit 4 as its execution engine. It made perfect sense: you really don’t want to waste time re-writing all of that boring-yet-necessary infrastructure (test runners, result reporters, build tool plugins, IDE plugins, etc.), and piggy-backing on existing solutions lowers the barrier for adoption of a new tool like Specnaz considerably.
JUnit 4 was the obvious choice; it’s to this day the most popular Java testing framework, and its Runner API makes implementing custom testing solutions a breeze, even ones who look so radically different from the “standard” JUnit tests like Specnaz ones do.
However, even since the beginning, I structured the project in a way that clearly separated the core logic of executing a Specnaz spec from the JUnit 4 integration code, anticipating that JUnit 4 might not always be the only supported execution engine. This paid dividends last year, when, by popular demand, I was able to add support for running Specnaz specs with TestNG.
While JUnit 4 was the obvious choice back in 2016, today that same choice would be anything but obvious. JUnit 4 is effectively abandonware, having had its last stable release in 2014. Its development team is completely focused on its successor, JUnit 5, which, after spending 2 years in beta, finally had a General Availability release in September of 2017.
Since then, the momentum has visibly shifted to JUnit 5 as the future,
and JUnit 4 being relegated pretty much exclusively to legacy projects.
With this momentum shift,
it was important that Specnaz follow suit,
as I don’t want the project to be seen as only working with old and crufty technologies.
So, with the release of version 1.5
,
Specnaz now supports JUnit 5 as the third test execution engine.
JUnit 5 with Java
To use the JUnit 5 support in Java,
you need to depend on the new module specnaz-junit-platform
instead of the JUnit 4 specnaz-junit
one.
In your spec class, you do the usual thing:
implement the Specnaz
interface,
and then the call the describes
method in the
public
, no-argument constructor of the class.
The only JUnit 5-specific thing you need to do is annotate the class with the
Testable
annotation from the org.junit.platform.commons.annotation
package in the junit-platform
module.
Here’s an example of the StackSpec
from the main Specnaz ReadMe using JUnit 5:
import org.junit.platform.commons.annotation.Testable;
import org.specnaz.Specnaz;
import java.util.Stack;
import static org.assertj.core.api.Assertions.assertThat;
@Testable
public class StackSpec implements Specnaz {{
describes("A Stack", it -> {
Stack<Integer> stack = new Stack<>();
it.endsEach(() -> {
stack.clear();
});
it.should("be empty when first created", () -> {
assertThat(stack).isEmpty();
});
it.describes("with 10 and 20 pushed on it", () -> {
it.beginsEach(() -> {
stack.push(10);
stack.push(20);
});
it.should("have size equal to 2", () -> {
assertThat(stack).hasSize(2);
});
it.should("have 20 as the top element", () -> {
assertThat(stack.peek()).isEqualTo(20);
});
});
});
}}
Parametrized tests are pretty much identical;
the only difference is implementing the SpecnazParams
interface instead of Specnaz
.
Everything else, including the @Testable
annotation,
remain the same.
JUnit 5 in Kotlin
The JUnit 5 support in Kotlin is very similar to the Java one.
There is the new specnaz-kotlin-junit-platform
module that you need to depend on,
and your spec class has to implement the SpecnazKotlin
interface instead of Specnaz
,
like always when writing specs in Kotlin.
Other than that, things are pretty much identical to the Java experience:
you annotate your class with the @Testable
,
and call the describes
method in the constructor, as usual:
import org.junit.platform.commons.annotation.Testable
import org.specnaz.kotlin.SpecnazKotlin
import java.util.Stack
import org.assertj.core.api.Assertions.assertThat
@Testable
class StackSpec : SpecnazKotlin {
init {
describes("A Stack") {
var stack = Stack<Int>()
it.endsEach {
stack = Stack()
}
it.should("be empty when first created") {
assertThat(stack).isEmpty()
}
it.describes("with 10 and 20 pushed on it") {
it.beginsEach {
stack.push(10)
stack.push(20)
}
it.should("have size equal to 2") {
assertThat(stack.size).isEqualTo(2)
}
it.should("have 20 as the top element") {
assertThat(stack.peek()).isEqualTo(20)
}
}
}
}
}
In addition, there is a SpecnazKotlinJUnitPlatform
class,
which is analogous to the SpecnazKotlinJUnit
class from the specnaz-junit
module and the SpecnazKotlinTestNG
class from the specnaz-testng
module.
It implements the SpecnazKotlin
interface,
is already annotated with the @Testable
annotation,
and calls the describes
method in its primary constructor –
which means you can save a little boilerplate code,
and some indentation,
if your spec class does not need to extend a particular class.
Here’s the same StackSpec
class above, but using SpecnazKotlinJUnitPlatform
:
import org.specnaz.kotlin.junit.platform.SpecnazKotlinJUnitPlatform
import java.util.Stack
import org.assertj.core.api.Assertions.assertThat
class StackKotlinSpec : SpecnazKotlinJUnitPlatform("A Stack", {
var stack = Stack<Int>()
// the spec body is the same as above...
})
Parametrized tests are very similar to their Java counterparts:
you either implement the SpecnazKotlinParams
interface and annotate your class with the @Testable
annotation,
or you extend the SpecnazKotlinParamsJUnitPlatform
class from the org.specnaz.kotlin.params.junit.platform
package.
Further information
For more details about the JUnit 5 support, check out the Specnaz reference documentation on the topic. There is also an examples directory in the main distribution that contains some simple working tests with JUnit 5.