Merge pull request #217 from denghuafeng/dev

nutzboot-starter-test 基于 JUnit 5 实现单元测试
This commit is contained in:
Wendal Chen 2020-07-20 16:32:50 +08:00 committed by GitHub
commit 6f8ad35f9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 599 additions and 1 deletions

View File

@ -0,0 +1,115 @@
# nutzboot-starter-test 基于 JUnit 5 实现单元测试
简介(可用性:测试,维护者:邓华锋([http://dhf.me](http://dhf.me)))
==================================
添加依赖
本插件依赖
```xml
<dependency>
<groupId>org.nutz</groupId>
<artifactId>nutzboot-starter-test</artifactId>
<version>2.4.1-SNAPSHOT</version>
<scope>test</scope>
</dependency>
```
JUnit5依赖
```xml
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-migrationsupport</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
```
使用示例
----------------------------------------------
使用@NutzBootTest注解进行单元测试。
```Java
package org.nutz.boot.test;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.nutz.boot.test.entity.UserDo;
import org.nutz.boot.test.service.UserService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.lang.Strings;
import java.util.stream.Stream;
@NutzBootTest
public class TestUserService {
@Inject
UserService userService;
static Stream<UserDo> addUser() {
UserDo[] users=new UserDo[5];
for (int i = 0; i <users.length ; i++) {
UserDo user=new UserDo();
user.setId("Junit5-"+i);
user.setName("测试-"+i);
users[i]=user;
}
return Stream.of(users);
}
@ParameterizedTest
@Tag("user")
@DisplayName("增加用户")
@MethodSource("addUser")
public void add(UserDo user) {
if (Strings.isBlank(user.getId())) {
return;
}
userService.save(user);
}
}
```
后期功能待更新。

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.nutz</groupId>
<artifactId>nutzboot-starter</artifactId>
<version>2.4.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>nutzboot-starter-test</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-migrationsupport</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,129 @@
package org.nutz.boot.test;
import org.junit.jupiter.api.extension.*;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
import org.junit.jupiter.api.extension.ExtensionContext.Store;
import org.nutz.boot.AppContext;
import org.nutz.boot.NbApp;
import org.nutz.ioc.Ioc;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.lang.Strings;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import java.lang.reflect.Field;
public class NutzBootExtension implements BeforeAllCallback, AfterAllCallback, TestInstancePostProcessor,
BeforeEachCallback, AfterEachCallback, BeforeTestExecutionCallback, AfterTestExecutionCallback,
ParameterResolver {
private final static Log log = Logs.get();
/**
* {@link Namespace} in which {@code TestContextManagers} are stored,
* keyed by test class.
*/
private static final Namespace NAMESPACE = Namespace.create(NutzBootExtension.class);
@Override
public void beforeAll(ExtensionContext context) throws Exception {
getTestContextManager(context).execute();
}
@Override
public void afterAll(ExtensionContext context) throws Exception {
try {
getTestContextManager(context).shutdown();
}
finally {
getStore(context).remove(context.getRequiredTestClass());
}
}
@Override
public void afterEach(ExtensionContext context) throws Exception {
}
@Override
public void afterTestExecution(ExtensionContext context) throws Exception {
}
@Override
public void beforeEach(ExtensionContext context) throws Exception {
}
@Override
public void beforeTestExecution(ExtensionContext context) throws Exception {
}
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
return false;
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
return null;
}
@Override
public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception {
_init_fields(getApplicationContext(context).getIoc(),testInstance);
}
/**
* 遍历当前对象中的属性,如果标注了@Inject则从ioc容器取出对象,注入进去
* @throws Exception 注入过程中如果抛出异常
*/
public void _init_fields(Ioc ioc,Object testInstance) throws Exception {
Field[] fields = testInstance.getClass().getDeclaredFields();
for (Field field : fields) {
Inject inject = field.getAnnotation(Inject.class);
if (inject == null) {
continue;
}
String val = inject.value();
Object v = null;
if (Strings.isBlank(val)) {
v = ioc.get(field.getType(), field.getName());
} else {
if (val.startsWith("refer:")) {
val = val.substring("refer:".length());
}
v = ioc.get(field.getType(), val);
}
field.setAccessible(true);
field.set(testInstance, v);
}
}
/**
* Get the {@link AppContext} associated with the supplied {@code ExtensionContext}.
* @param context the current {@code ExtensionContext} (never {@code null})
* @return the application context
* @throws IllegalStateException if an error occurs while retrieving the application context
* //@see org.springframework.test.context.TestContext#getApplicationContext()
*/
public static AppContext getApplicationContext(ExtensionContext context) {
return getTestContextManager(context).getAppContext();
}
/**
* Get the {@link NbApp} associated with the supplied {@code ExtensionContext}.
* @return the {@code NbApp} (never {@code null})
*/
private static NbApp getTestContextManager(ExtensionContext context) {
//Assert.notNull(context, "ExtensionContext must not be null");
Class<?> testClass = context.getRequiredTestClass();
Store store = getStore(context);
return store.getOrComputeIfAbsent(testClass, NbApp::new, NbApp.class).setPrintProcDoc(true);
}
private static Store getStore(ExtensionContext context) {
return context.getRoot().getStore(NAMESPACE);
}
}

View File

@ -0,0 +1,93 @@
package org.nutz.boot.test;
import org.junit.jupiter.api.extension.ExtendWith;
import java.lang.annotation.*;
/**
* @author 邓华锋
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ExtendWith(NutzBootExtension.class)
public @interface NutzBootTest {
/**
* Alias for {@link #properties()}.
* @return the properties to apply
*/
String[] value() default {};
/**
* Properties in form key=value that should be added to the Nutzboot
* Environment before the test runs.
* @return the properties to add
*/
String[] properties() default {};
/**
* Application arguments that should be passed to the application under test.
* @return the application arguments to pass to the application under test.
*/
String[] args() default {};
Class<?>[] classes() default {};
/**
* The type of web environment to create when applicable. Defaults to
* {@link WebEnvironment#MOCK}.
* @return the type of web environment
*/
WebEnvironment webEnvironment() default WebEnvironment.MOCK;
/**
* 测试机哦u偶下
*/
enum WebEnvironment {
/**
* mock
*/
MOCK(false),
/**
* 随机端口
*/
RANDOM_PORT(true),
/**
* defina port
*/
DEFINED_PORT(true),
/**
* 默认配置
*/
NONE(false);
/**
* 是否tomcat
*/
private final boolean embedded;
/**
* 是否tomcat
* @param embedded
*/
WebEnvironment(boolean embedded) {
this.embedded = embedded;
}
/**
* 获取环境
* @return
*/
public boolean isEmbedded() {
return this.embedded;
}
}
}

View File

@ -0,0 +1,43 @@
package org.nutz.boot.test;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.nutz.boot.test.entity.UserDo;
import org.nutz.http.Http;
import org.nutz.json.Json;
import org.nutz.lang.util.NutMap;
import java.util.Map;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertTrue;
@NutzBootTest
public class TestUserModule {
static Stream<UserDo> addUser() {
UserDo[] ServiceResouresDos=new UserDo[5];
for (int i = 0; i <ServiceResouresDos.length ; i++) {
UserDo serviceResoures=new UserDo();
serviceResoures.setId("STest123-"+i);
serviceResoures.setName("测试服务-"+i);
ServiceResouresDos[i]=serviceResoures;
}
return Stream.of(ServiceResouresDos);
}
@ParameterizedTest
@Tag("user")
@DisplayName("增加用户")
@MethodSource("addUser")
void test_service_resources_add(UserDo argument){
Map<String,Object> params= Json.fromJson(Map.class, Json.toJson(argument));
String content= Http.post("http://localhost:8088/user/add",params,10000);
NutMap result = Json.fromJson(NutMap.class,content);
assertTrue(result.getBoolean("ok"));
}
}

View File

@ -0,0 +1,40 @@
package org.nutz.boot.test;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.nutz.boot.test.entity.UserDo;
import org.nutz.boot.test.service.UserService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.lang.Strings;
import java.util.stream.Stream;
@NutzBootTest(webEnvironment = NutzBootTest.WebEnvironment.RANDOM_PORT)
public class TestUserService {
@Inject
UserService userService;
static Stream<UserDo> addUser() {
UserDo[] ServiceResouresDos=new UserDo[5];
for (int i = 0; i <ServiceResouresDos.length ; i++) {
UserDo serviceResoures=new UserDo();
serviceResoures.setId("STest123-"+i);
serviceResoures.setName("测试服务-"+i);
ServiceResouresDos[i]=serviceResoures;
}
return Stream.of(ServiceResouresDos);
}
@ParameterizedTest
@Tag("user")
@DisplayName("增加用户")
@MethodSource("addUser")
public void add(UserDo user) {
if (Strings.isBlank(user.getId())) {
return;
}
userService.save(user);
}
}

View File

@ -0,0 +1,24 @@
package org.nutz.boot.test.entity;
import java.io.Serializable;
public class UserDo implements Serializable {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,30 @@
package org.nutz.boot.test.module;
import org.nutz.boot.test.entity.UserDo;
import org.nutz.boot.test.service.UserService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.*;
import org.nutz.mvc.filter.CrossOriginFilter;
@At("/user")
@IocBean
@Ok("json:full")
public class UserModule {
@Inject
UserService userService;
@At
@POST
@Filters({@By(type= CrossOriginFilter.class)})
public NutMap add(@Param("..") UserDo user) {
if (Strings.isBlank(user.getId())) {
return new NutMap("ok", false);
}
userService.save(user);
return new NutMap("ok", true).setv("data", user);
}
}

View File

@ -0,0 +1,7 @@
package org.nutz.boot.test.service;
import org.nutz.boot.test.entity.UserDo;
public interface UserService {
void save(UserDo user);
}

View File

@ -0,0 +1,13 @@
package org.nutz.boot.test.service.impl;
import org.nutz.boot.test.entity.UserDo;
import org.nutz.boot.test.service.UserService;
import org.nutz.ioc.loader.annotation.IocBean;
@IocBean(name="userService")
public class UserServiceImpl implements UserService {
@Override
public void save(UserDo user) {
//TODO some thing
}
}

View File

@ -0,0 +1,4 @@
server.port=8088
nutz.boot.configure.properties.dir=
nutz.scans.paths=
z

View File

@ -82,7 +82,7 @@
<module>nutzboot-starter-thrift-server</module>
<module>nutzboot-starter-webjars</module>
<module>nutzboot-starter-swagger3</module>
<module>nutzboot-starter-test</module>
</modules>
<dependencies>
<dependency>

45
pom.xml
View File

@ -54,6 +54,9 @@
<swagger-servlet.version>1.6.0</swagger-servlet.version>
<swagger3.version>2.1.1</swagger3.version>
<shardingsphere.version>4.1.0</shardingsphere.version>
<junit-jupiter.version>5.6.2</junit-jupiter.version>
<hamcrest.version>[2.2,)</hamcrest.version>
<mockito.version>[1.10.19,)</mockito.version>
</properties>
<description>NutzBoot, micoservice base on Nutz</description>
@ -1024,6 +1027,48 @@
<artifactId>nacos-client</artifactId>
<version>${nacos.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit-jupiter.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit-jupiter.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit-jupiter.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-migrationsupport</artifactId>
<version>${junit-jupiter.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>${junit-jupiter.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>${hamcrest.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>