Junit 单元测试
# Junit 单元测试
# 目录
- Junit4 单元测试
- Junit5 单元测试
- @TestConfig
- @MockMethod
- @MockClass
# Junit 单元测试简介
单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。
JUnit 是一个 Java 编程语言的单元测试框架。JUnit 在测试驱动的开发方面有很重要的发展,是起源于 JUnit 的一个统称为 xUnit 的单元测试框架之一。
目前市面上主要是使用 Junit4 和 Junit5 对 Java 程序进行单元测试。
# Junit4 单元测试
1、第一步,添加 junit4 的 maven 依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
2、第二步,编写单元测试代码
@RunWith(JbootRunner.class)
public class MyAppTester {
private static MockMvc mvc = new MockMvc();
@Inject
private MyService myService;
@Test
public void test_url_aaa() {
MockMvcResult mvcResult = mvc.get("/aaa");
mvcResult.printResult()
.assertThat(result -> Assert.assertNotNull(result.getContent()))
.assertTrue(result -> result.getStatus() == 200);
}
@Test
public void test_url_bbb() {
MockMvcResult mvcResult = mvc.get("/bbb");
mvcResult.printResult()
.assertThat(result -> Assert.assertNotNull(result.getContent()))
.assertTrue(result -> result.getStatus() == 200);
}
@Test
public void test_my_service() {
Ret ret = myService.doSomeThing();
Assert.assertNotNull(ret);
//.....
}
}
注意:Junit4 测试类必须添加
@RunWith(JbootRunner.class)
配置
# Junit5 单元测试
1、第一步,添加 junit4 的 maven 依赖
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.7.2</version>
<scope>test</scope>
</dependency>
2、第二步,编写单元测试代码
@ExtendWith(JbootExtension.class)
public class MyAppTester {
private static MockMvc mvc = new MockMvc();
@Inject
private MyService myService;
@Test
public void test_url_aaa() {
MockMvcResult mvcResult = mvc.get("/aaa");
mvcResult.printResult()
.assertThat(result -> Assertions.assertNotNull(result.getContent()))
.assertTrue(result -> result.getStatus() == 200);
}
@Test
public void test_url_bbb() {
MockMvcResult mvcResult = mvc.get("/bbb");
mvcResult.printResult()
.assertThat(result -> Assertions.assertNotNull(result.getContent()))
.assertTrue(result -> result.getStatus() == 200);
}
@Test
public void test_my_service() {
Ret ret = myService.doSomeThing();
Assertions.assertNotNull(ret);
//.....
}
}
注意:Junit5 测试类必须添加
@ExtendWith(JbootExtension.class)
配置
# @TestConfig
在测试的过程中,Jboot 默认的 webRootPath 是 target/classes/webapp
目录,而 classPath 的目录是 target/classes
目录。
如果我们需要修改此目录,则需要在测试类中添加 @TestConfig 注解,对 webRootPath 或 classPath 进行配置。
例如:
@RunWith(JbootRunner.class)
@TestConfig(webRootPath = "your-path",classPath = "your-path")
public class MyAppTester {
private static MockMvc mvc = new MockMvc();
@Test
public void test_url_aaa() {
MockMvcResult mvcResult = mvc.get("/aaa");
//your code ...
}
}
@TestConfig(webRootPath = "your-path",classPath = "your-path")
里的配置路径,可以是绝对路径或相对路径,
若是相对路径,则是相对 target/test-classes
目录的路径。
# @MockMethod
Jboot 提供了 @MockMethod
注解,方便对 AOP 管理的类里的方法(method)进行 Mock 操作。
例如:
@RunWith(JbootRunner.class)
@TestConfig(autoMockInterface = true)
public class OptionApiControllerTest {
private static final MockMvc mvc = new MockMvc();
@Test
public void query() {
mvc.get("/api/option/query?key=myKey").printResult();
}
@MockMethod(targetClass = JPressCoreInitializer.class)
public void onHandlerConfig(JfinalHandlers handlers) {
handlers.add(new JPressHandler());
}
@MockMethod(targetClass = UtmService.class)
public void doRecord(Utm utm){
System.out.println(">>>>>>>>>doRecord: " + utm);
}
@MockMethod(targetClass = WebInitializer.class,targetMethod = "onEngineConfig")
public void mock_on_engine_config(Engine engine){
System.out.println(">>>>>>>>>onEngineConfig: " + engine);
}
}
在以上的代码中,有几个关键的地方
@TestConfig(autoMockInterface = true)
,表示当我们测试query()
方法的时候,可能会遇到一些注入进来的接口,但是可能没有实现类(或者说实现类在别的 Maven Module,并没有依赖进来),但是保证不出错。 当调用接口方法时,等同于什么都不做,若有返回值,则返回null
。@MockMethod(targetClass = JPressCoreInitializer.class)
表示复写JPressCoreInitializer
类的onHandlerConfig
方法。@MockMethod(targetClass = UtmService.class)
表示复写UtmService
类的doRecord
方法。@MockMethod(targetClass = WebInitializer.class,targetMethod = "onEngineConfig")
表示复写WebInitializer
类的onEngineConfig
方法。
注意:
1、在以上代码中,
mock_on_engine_config
方法的参数必须和WebInitializer.onEngineConfig
里的参数一样, 或者mock_on_engine_config
可以多出一个targetClass
的对象参数,例如:mock_on_engine_config(WebInitializer webInitializer,Engine engine)2、
@MockMethod
的优先级高于@TestConfig(autoMockInterface = true)
的配置。
# @MockClass
Jboot 提供了 @MockClass
注解,方便对 AOP 管理的类(Class)进行 Mock 操作。
例如,如下的代码是请求了 /api/article/detail
这个 API 接口。
public class ArticleApiControllerTest{
static final MockMvc mvc = new MockMvc();
@Test
public void detail() {
mvc.get("/api/article/detail?id=1").printResult();
}
}
假设这个 API 接口里的 Controller
通过 ArticleService
进行进步一查询数据。除了我们可以通过 @MockMethod
对 ArticleService
的方法进行 Mock,
也编写一个类,实现 ArticleService
接口,并通过 @MockClass
添加在实现的类上。
@MockClass
public class ArticleServiceMock implements ArticleService {
@Override
public Article findById(Object id) {
Article article = new Article();
article.setId((Long) id);
article.setStatus(Article.STATUS_NORMAL);
return article;
}
}
此时,我们在测试的时候,当 Controller
调用了 ArticleService.findById(id)
就会自动调用了 ArticleServiceMock
里的 findById
方法。
相对 @MockMethod
而言,@MockClass
有几个好处:
- 1、
@MockClass
注解的类是去实现某个接口的,ide 等工具会自动帮我们生成方法,不易出错。 - 2、当 Mock 很多个方法的时候,可以写到这个类里,使得测试代码更加简洁
- 3、在有多个测试类的时候,多个测试类可以共用相同的 Mock 代码,减少代码量
注意:如果
@MockClass
和@MockMethod
同时对一个方法进行 Mock,@MockMethod
的优先级高于@MockClass
。