assertThat(account.getBalance()).isEqualTo(new Amount(99));
assertThat(account.getConsumeRecords())
.hasSize(1)
.hasOnlyOneElementSatisfying(record ->
assertThat(record.getAmount())
.isEqualTo(new Amount(1)));
BDD风格
Spock最大亮点之一,Given-When-Than,不解释,代码一目了然:
def "HashMap accepts null key"() {
given:"一个HashMap"
def map = new HashMap()
when:"给它添加null元素"
map.put(null, "elem")
then:"不抛出异常"
notThrown(NullPointerException)
可以实现“Specifications as Documentation”的效果,也可以生成非常好的报告。
JUnit5使用@Displayname和@Nested也可以勉强应对吧:
image.png
运行结果:
image.png
image.png
数据驱动测试
Spock的另一个大亮点,不解释看代码:
import spock.lang.Unroll
class DataDrivenTest extends Specification {
@Subject def arithmeticOperation = new ArithmeticOperation()
@Unroll
def "can add"() {
expect:
arithmeticOperation.add(1, b) >= 2
where:
b << [1, 2, 3, 4, 5]
@Unroll
def "can add #a to #b and receive #result"() {
expect:
arithmeticOperation.add(a, b) == result
where:
a | b | result
1 | 3 | 4
3 | 4 | 7
10 | 20 | 30
执行效果:
JUnit5怎么实现呢?
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import java.util.stream.Stream;
public class DataDrivenTest {
private final ArithmeticOperation arithmeticOperation = new ArithmeticOperation();
@ParameterizedTest
@ValueSource(ints = { 1, 2, 3, 4, 5 })
void canAdd(int b) {
assertTrue(arithmeticOperation.add(1, b) >= 2);
@ParameterizedTest(name = "can add {0} to {1} and receive {2}")
@MethodSource("additionProvider")
void canAddAndAssertExactResult(int a, int b, int result) {
assertEquals(result, arithmeticOperation.add(a, b));
static Stream<Arguments> additionProvider() {
return Stream.of(
Arguments.of(1, 3, 4),
Arguments.of(3, 4, 7),
Arguments.of(10, 20, 30)
运行效果:
image.png
此外,还有Mock/Stub、按条件执行测试等功能,如果大家有兴趣看的话,后续再写。
总之,可以看到,JUnit5在表达性上,已经在努力追赶Spock了,但局限于Java语法的局限,还是有不小的差距的。但相对于JUnit4,JUnit5进步已经非常大了。如果你尤其是你的团队不想学习Spock/Groovy的话,或者喜欢IDE对Java更好的提示的话,那最起码可以考虑用JUnit5代替JUnit4了。