在参数化测试方面,JUnit5提供了较为丰富的数据源,如@ValueSource,支持提供int、float等基本类型以及String和Class等作为参数,@CsvSource可以提供CSV格式的数据。另外还可以通过@MethodSource来提供任意类型的数据。
除了上述由JUnit5提供的数据源之外,JUnit也接受自定义数据源来进行参数化测试。在扩展时,只需要实现下述接口即可,
public interface ArgumentsProvider {
Stream<? extends Arguments> provideArguments(ExtensionContext var1)throws Exception;
}
案例1-字符串为空
为字符串为空的场景设计几个入参数据
package com.github.junit5.parameter;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import java.util.stream.Stream;
public class BlankStringsArgumentsProvider implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return Stream.of(
Arguments.of((String) null),
Arguments.of(""),
Arguments.of(" ")
);
}
}
然后,在用例中申明自定义数据源为BlankStringsArgumentsProvider,就可以使用上述数据源了。
package com.github.junit5.parameter;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource;
import static org.assertj.core.api.Assertions.assertThat;
public class CustomParamsTest {
@ParameterizedTest
@ArgumentsSource(BlankStringsArgumentsProvider.class)
void testBlank(String input) {
assertThat(input).isBlank();
}
}
这只是一个最简单的案例。接下来将结合金融系统的案例来介绍一个更为贴合实际的场景。
案例2-价格有效性检查
在金融行业里面,价格不连续的情况非常常见。例如,以下是某个证券交易所的股票的最小变动价位表,显示相关股票能够交易的最小价位变动,
股票价格 | 最小变动价位(tick) |
---|---|
10.00至20.00 | 0.020 |
20.00至100.00 | 0.050 |
100.00至200.00 | 0.100 |
200.00至500.00 | 0.200 |
如果股票价格在200-500元的区间内,最小价格变动为0.2元。也就是说,当进行交易时,报价必须是0.2(tick)的整数倍。如果当前的价格点位是300点,则最小的变动价格是300.2或者是299.8。
那么在进行报单的有效性检查时,其中一个典型的检查是,价格是否是最小变动价位(tick)的整数倍。从数学的角度来讲,这个问题就转换成了一个简单的数学问题:
如何判断一个数是另外一个数的整数倍?
那么问题来了,在现实中一般交易所的交易系统代码是用C++编写,并没有使用类似JAVA BigDecimal的库来专门处理数学运算。以某交易所对外发布的接口说明文档为例,
///最小变动价位
TThostFtdcPriceType PriceTick;
///本次结算价
TThostFtdcPriceType SettlementPrice;
///涨停板价
TThostFtdcPriceType UpperLimitPrice;
///跌停板价
TThostFtdcPriceType LowerLimitPrice;
//////////////////////////////////////
///TFtdcPriceType是一个价格类型
//////////////////////////////////////
typedef double TThostFtdcPriceType;
众所周知,浮点数在计算机中为近视值。比如价格,客户端输入的价格为300.2,但是实际存储的值为300.19999999999996615。简单的利用两数取余是否为零来判断是否整除,由于精度问题无法实现。
这样,需要对价格有效性判断的程序进行至少两方面的测试:
使用基于JUnit5自定义数据源的开源项目junit-pioneer就支持这样的测试场景。
public class TestTick {
static final double tick =0.2;
TickCheckRule rule = new TickCheckRule(tick);
@ParameterizedTest
@DoubleRangeSource(from = 200, to = 300, step = 0.2)
void testValidTick(double price) throws Exception {
assertThat(rule.checkTick(price)).isTrue();
} @ParameterizedTest
@DoubleRangeSource(from = 200.001, to = 200.199, step = 0.001)
void testInvalidTick(double price) throws Exception {
assertThat(rule.checkTick(price)).isFalse();
}}
在JUnit5中,定义了ArgumentsProvider这个接口以用于自定义的数据源。junit-pioneer正是通过RangeSourceArgumentsProvider来实现这一接口,可以实现了对这种规定起止点后按步距增长的参数化测试场景。对其实现感兴趣的读者可以阅读RangeSourceArgumentsProvider这个类的源码以了解其具体实现。
该项目托管在https://github.com/junit-pioneer/junit-pioneer。
案例3-@JsonSource
除了@CsvSource和@CsvFileSource来读取CSV格式的入参之外,在工作中也可能希望是以JSON格式的数据来实施参数化测试,毕竟JSON类型的数据已经成为了系统接口之间交换数据的主流方式。这也意味着此类数据更加容易获得。那除了通过@MethodSource方法来实现之外,也可以以自定义数据源的方式来实现。对此感兴趣的读者可以参考junit-json-params这个开源项目(https://github.com/joshka/junit-json-params)。以下是其一个使用案例,
@ParameterizedTest
@JsonSource("[{\"key\":\"value1\"},{\"key\":\"value2\"}]")
public void arrayOfObjects(JsonObject object) {
assertThat(object.getString("key")).startsWith("value");
}
通过扩展ArgumentsProvider 接口来实现自定义数据源,你会了吗?