junit5与junit4区别比较大,junit5使用了大量的jdk8特性,lambda表达式,使用junit5前需要自行脑补jdk8特性
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
spring boot2.x使用junit5需要使用注解 @ExtendWith(SpringExtension.class),下面的例子使用junit5的参数化测试的功能,即配置测试参数数据源,这里我没有使用junit5的 csv数据源,我采用了自定义扩展数据源方式,使用json数据源的方式。
package com.xxx.junit5.test;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import com.alibaba.fastjson.JSONObject;
import com.xxx.spring.SpringBootStarter;
import com.xxx.user.model.UserVO;
/**
* junit5测试用例
*
* @author xuchang
*/
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = { SpringBootStarter.class })
@AutoConfigureMockMvc
@ExtendWith(SpringExtension.class)
public class TestBizAccount4Junit5 {
/** */
@Autowired
private MockMvc mockMvc;
@Override
protected MockMvc getMockMvc() {
return mockMvc;
}
/**
* 费用报销单保存测试
*
* 请求URI: "/bizaccount/save"
*
* @param testData xx
*
* @throws Exception 异常
*/
@ParameterizedTest
@JsonFileSource(resources = "/com/xxx/junit5/test/testData.json")
void testSave(JSONObject testData) throws Exception {
UserVO param = new UserVO();
param.setName(testData.getString("name"));
param.setNumber(testData.getString("number"));
ResultActions resultActions = this.getMockMvc()
.perform(get("/user/testConsumes/{ss}", "xxx").contentType(MediaType.APPLICATION_JSON_UTF8)
.content(JSONObject.toJSONString(testData)).accept(MediaType.ALL))
.andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andDo(print());
resultActions.andReturn();
}
}
自定义扩展JSON数据源
package com.xxx.junit5.test;
import static org.apiguardian.api.API.Status.EXPERIMENTAL;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.apiguardian.api.API;
import org.junit.jupiter.params.provider.ArgumentsSource;
/**
* junit5 JSON文件测试数据源
*
* @author xuchang
*/
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = EXPERIMENTAL, since = "5.0")
@ArgumentsSource(JsonFileArgumentsProvider.class)
public @interface JsonFileSource {
/**
* JSON资源文件路基
*
*/
String[] resources();
/**
* json文件解析编码格式
*
*
* Defaults to {@code "UTF-8"}.
*
* @see java.nio.charset.StandardCharsets
*/
String encoding() default "UTF-8";
}
package com.xxx.junit5.test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.function.BiFunction;
import java.util.stream.Stream;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.support.AnnotationConsumer;
import org.junit.platform.commons.util.PreconditionViolationException;
import org.junit.platform.commons.util.Preconditions;
import com.alibaba.fastjson.JSONObject;
/**
* JSON文件参数提供者
*
* @author xuchang
*/
public class JsonFileArgumentsProvider implements ArgumentsProvider, AnnotationConsumer {
private final BiFunction, String, InputStream> inputStreamProvider;
private JsonFileSource annotation;
private String[] resources;
private Charset charset;
JsonFileArgumentsProvider() {
this(Class::getResourceAsStream);
}
JsonFileArgumentsProvider(BiFunction, String, InputStream> inputStreamProvider) {
this.inputStreamProvider = inputStreamProvider;
}
@Override
public void accept(JsonFileSource annotation1) {
this.annotation = annotation1;
resources = annotation1.resources();
try {
this.charset = Charset.forName(annotation1.encoding());
} catch (Exception ex) {
throw new PreconditionViolationException("The charset supplied in " + this.annotation + " is invalid", ex);
}
}
@Override
public Stream extends Arguments> provideArguments(ExtensionContext context) throws Exception {
return Arrays.stream(resources).map(resource -> openInputStream(context, resource)).flatMap(this::toStream);
}
private Stream toStream(InputStream inputStream) {
JSONObject obj = JSONObject.parseObject(readJSON(inputStream), JSONObject.class);
return Stream.of(Arguments.of(obj));
}
/**
* 读取文件到字符串
*
* @param file 文件
* @return 文件内容
*/
private String readJSON(InputStream inputStream) {
StringBuilder objFile = new StringBuilder(1000);
// 读取系统换行符
BufferedReader objReader = null;
try {
objReader = new BufferedReader(new InputStreamReader(inputStream, charset));
String strLine = objReader.readLine();
while (strLine != null) {
objFile.append(strLine).append(System.getProperty("line.separator"));
strLine = objReader.readLine();
}
} catch (IOException e) {
System.out.println(e.getMessage());
}
return objFile.toString();
}
private InputStream openInputStream(ExtensionContext context, String resource) {
Preconditions.notBlank(resource, "Classpath resource [" + resource + "] must not be null or blank");
Class> testClass = context.getRequiredTestClass();
return Preconditions.notNull(inputStreamProvider.apply(testClass, resource),
() -> "Classpath resource [" + resource + "] does not exist");
}
public JsonFileSource getAnnotation() {
return annotation;
}
public void setAnnotation(JsonFileSource annotation) {
this.annotation = annotation;
}
public String[] getResources() {
return resources;
}
public void setResources(String[] resources) {
this.resources = resources;
}
public Charset getCharset() {
return charset;
}
public void setCharset(Charset charset) {
this.charset = charset;
}
public BiFunction, String, InputStream> getInputStreamProvider() {
return inputStreamProvider;
}
}