你好!
我回到本教程中,教您如何使用Spring Boot开发REST API。正如我之前说的,本教程分为5个部分,如下表所示:
部分 | 内容 |
---|---|
第01部分 | Application Startup |
第02部分 | Settings and Database |
第03部分 | API Implementation |
第04部分 | 测试 +覆盖范围 |
第05部分 | Swagger Documentation |
可以预期,从这第四部分,您将学习以下内容:
- 使用junit
- 创建单位测试
- 创建集成测试
- 使用Fakers创建测试数据
请记住,在本教程中使用以下工具:
- JDK 17
- Smart
- Maven
- 失眠
- mySQL数据库
步骤01-创建测试属性
通常,当我们必须测试应用程序时,我们会创建一个专用于测试的属性文件。这样做是为了建立更好的开发和测试环境。
因此,在resources
内部,您可以创建一个名为application-test.properties
的新文件,该文件将包含以下内容:
spring.datasource.url=jdbc:mysql://localhost:3307/small_library_test
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
前三个项目是数据库连接的原因。请注意,已经为测试创建了一个新的数据库(small_library_test
),但是如果需要,可以使用与开发的相同数据库。
最后一个物品是为了避免LazyInitializationException
异常。
因此,当我们创建测试时,它们将使用此新文件中定义的属性。
步骤02-异常处理程序
默认情况下,Spring通过类处理一些例外,例如EntityNotFoundException
和EmptyResultDataAccessException
。
当用户尝试在数据库中搜索一个不存在的对象时,就会发生第一种情况,例如,他们想找到一本ID等于9999999999的书(考虑此ID不具有的情况存在于数据库中)。默认情况下,它返回错误500(不良请求)以及异常主体。
但是,让我们想,在这种情况下,我们不想返回错误500,而是要返回错误404(找不到)。我们如何进行此修改?
这就是为什么有RestControllerAdvice
,用于处理控制器中的异常。为了创建它,您必须转到src.main.java.com.simplelibrary.simplelibrary.API
软件包,并创建一个名为exception
的新软件包。在此内,创建一个名为ErrorHandler
的类:
package com.simplelibrary.simplelibraryAPI.exception;
import jakarta.persistence.EntityNotFoundException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.simplelibrary.simplelibraryAPI.dto.ErrorValidatorDTO;
@RestControllerAdvice
public class ErrorHandler {
@ExceptionHandler({EntityNotFoundException.class, EmptyResultDataAccessException.class})
public ResponseEntity error404(){
return ResponseEntity.notFound().build();
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity error400(MethodArgumentNotValidException exception){
var erros = exception.getFieldErrors();
return ResponseEntity.badRequest().body(erros.stream().map(ErrorValidatorDTO::new).toList());
}
}
为了使Spring认识到该类是REST控制器建议,您必须将@RestControllerAdvice
注释放在类初始化之前。
对于您要处理的每个例外,您必须将其放入@ExceptionHandler
注释中,说该例外将在下一个方法中处理。
请注意,error404
方法将处理通常丢弃500错误并导致他们丢弃404错误的异常。
error400
方法将处理异常方法ArgumentNotValidexception。例如,当用户尝试注册书籍而没有标题时,就会触发此异常。最初,他将收到的回应如下:
{
"timestamp": "2023-01-27T19:04:03.372+00:00",
"status": 400,
"error": "Bad Request",
"trace": "org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public org.springframework.http.ResponseEntity com.simplelibrary.simplelibraryAPI.controller.BookController.store(com.simplelibrary.simplelibraryAPI.dto.BookRequestDTO,org.springframework.web.util.UriComponentsBuilder) throws java.io.IOException: [Field error in object 'bookRequestDTO' on field 'title': rejected value [null]; codes [NotBlank.bookRequestDTO.title,NotBlank.title,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [bookRequestDTO.title,title]; arguments []; default message [title]]; default message [não deve estar em branco]] \r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:144)\r\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:181)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:148)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:884)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)\r\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1080)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:973)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1010)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:913)\r\n\tat jakarta.servlet.http.HttpServlet.service(HttpServlet.java:731)\r\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:884)\r\n\tat jakarta.servlet.http.HttpServlet.service(HttpServlet.java:814)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:223)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)\r\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)\r\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)\r\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)\r\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)\r\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:177)\r\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)\r\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)\r\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:119)\r\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\r\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)\r\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)\r\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:400)\r\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\r\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:859)\r\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1734)\r\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)\r\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)\r\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)\r\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\r\n\tat java.base/java.lang.Thread.run(Thread.java:833)\r\n",
"message": "Validation failed for object='bookRequestDTO'. Error count: 1",
"errors": [
{
"codes": [
"NotBlank.bookRequestDTO.title",
"NotBlank.title",
"NotBlank.java.lang.String",
"NotBlank"
],
"arguments": [
{
"codes": [
"bookRequestDTO.title",
"title"
],
"arguments": null,
"defaultMessage": "title",
"code": "title"
}
],
"defaultMessage": "must not be blank",
"objectName": "bookRequestDTO",
"field": "title",
"rejectedValue": null,
"bindingFailure": false,
"code": "NotBlank"
}
],
"path": "/books"
}
意识到发送给用户的信息量,实际上我们只希望他们知道哪个字段是错误的,而错误消息是什么。因此,我们将通过DTO进行您的治疗。
在src.main.java.com.simplelibrary.simplelibrary.API.dto
软件包中,我们将创建一个名为ErrorValidatorDTO
的记录:
package com.simplelibrary.simplelibraryAPI.dto;
import org.springframework.validation.FieldError;
public record ErrorValidatorDTO(String field, String message){
public ErrorValidatorDTO(FieldError error){
this(error.getField(), error.getDefaultMessage());
}
}
从那里我们将获得错误对象,并仅返回用户必要的信息:
[
{
"field": "title",
"message": "must not be blank"
}
]
步骤03-书籍工厂
对于我们的测试,有必要创建几本具有不同信息的书籍。进行此手动工作有点疲惫,因此我们可以使用社区创建的机制。
我们将使用的机制之一是Faker
。从中,我们将能够为字段生成随机值并创建我们的书对象。为此,您需要将以下依赖关系导入到pom.xml
文件中:
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>1.0.2</version>
</dependency>
通过执行此操作,我们可以创建将负责创建书籍对象的BookFactory
类(DAO和DTO请求模型):
在src.main.java.com.simplelibrary.simplelibrary.API.factory
包中,我们将创建一个名为BookFakerFactory
的类:
package com.simplelibrary.simplelibraryAPI.factory;
import com.github.javafaker.Faker;
import com.simplelibrary.simplelibraryAPI.dto.BookRequestDTO;
import com.simplelibrary.simplelibraryAPI.model.Book;
public class BookFakerFactory {
private Faker faker;
public BookFakerFactory(){
this.faker = new Faker();
}
public BookRequestDTO createBook(){
var book = new BookRequestDTO(faker.book().title(), faker.book().author(),
faker.number().randomDigit(), faker.number().digits(13), faker.number().randomDouble(1,1,10),
faker.random().nextBoolean());
return book;
}
public BookRequestDTO createBook(Book bookCreated){
var book = new BookRequestDTO(bookCreated);
return book;
}
public Book createBookModel(){
var bookDTO = new BookRequestDTO(faker.book().title(), faker.book().author(),
faker.number().randomDigit(), faker.number().digits(13), faker.number().randomDouble(1,1,10),
faker.random().nextBoolean());
var book = new Book(bookDTO);
return book;
}
public BookRequestDTO createBookIncomplete(){
var book = new BookRequestDTO(faker.book().title(), "",
faker.number().randomDigit(), faker.number().digits(13), faker.number().randomDouble(1,1,10),
faker.random().nextBoolean());
return book;
}
}
请注意,我们有4种方法,所有方法的目的是创建一个对象,该对象以后将用于测试。
-
createBook
方法将创建一个将用于测试请求的BookRequestDTO
对象。 -
createBook
方法(带有参数)将从先前创建的书籍中创建一个BookRequestDTO
对象。 -
createBookModel
方法将创建一个Book
对象。 -
createBookIncomplete
方法将创建一个没有作者信息的BookRequestDTO
对象。
请注意,所有方法均使用Faker
库。
步骤04-单元测试
我们准备创建测试。如果您在test
包中查看,已经有一个名为SimplelibraryApiApplicationTests
的类:
package com.simplelibrary.simplelibraryAPI;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SimplelibraryApiApplicationTests {
@Test
void contextLoads() throws Exception {
}
}
它将使用JUnit
进行测试。
注意一些重要的注释:
-
@SpringBootTest
表明该类将包含应测试的方法。 -
@Test
表示该方法应作为测试运行。
因此我们可以创建第一个单元测试。这将测试我们存储库的某些功能:在src.main.test.java.com.simplelibrary.simplelibrary.API
软件包内部,我们将创建一个称为unit
的软件包,其中一类称为BookTest
:
package com.simplelibrary.simplelibraryAPI.unit;
import com.simplelibrary.simplelibraryAPI.factory.BookFakerFactory;
import com.simplelibrary.simplelibraryAPI.repository.BookRepository;
import com.simplelibrary.simplelibraryAPI.service.BookService;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
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.test.context.ActiveProfiles;
import static org.assertj.core.api.Assertions.*;
import java.io.IOException;
@SpringBootTest
@ActiveProfiles("test")
public class BookTest {
@Autowired
private BookRepository repository;
@Autowired
private BookService service;
private BookFakerFactory bookFakerFactory;
public BookTest() {
this.bookFakerFactory = new BookFakerFactory();
}
}
注意@ActiveProfiles("test")
注释。它用于通知Spring,我们正在使用application-test.properties
文件中定义的属性。
我们可以考虑要完成的一些测试:
-
测试成功创建一本书。
-
测试要验证收集给定书的评级的服务是否正常工作(验证评级值是从现有书籍中填充的,并且不填充不存在的书籍)。
所有测试的课程看起来像这样:
package com.simplelibrary.simplelibraryAPI.unit;
import com.simplelibrary.simplelibraryAPI.factory.BookFakerFactory;
import com.simplelibrary.simplelibraryAPI.repository.BookRepository;
import com.simplelibrary.simplelibraryAPI.service.BookService;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
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.test.context.ActiveProfiles;
import static org.assertj.core.api.Assertions.*;
import java.io.IOException;
@SpringBootTest
@ActiveProfiles("test")
public class BookTest {
@Autowired
private BookRepository repository;
@Autowired
private BookService service;
private BookFakerFactory bookFakerFactory;
public BookTest() {
this.bookFakerFactory = new BookFakerFactory();
}
@Test
public void itShouldCreateABookSuccessfully(){
var book = bookFakerFactory.createBookModel();
repository.save(book);
var bookFinder = repository.findBookByisbn(book.getIsbn());
Assertions.assertThat(book).isEqualTo(bookFinder);
}
@Test
public void itShouldFillTheRatingValueFromExistingBook() throws IOException {
var book = bookFakerFactory.createBookModel();
book.setTitle("Harry Potter and the Philosopher's Stone");
var bookCreated = service.store(bookFakerFactory.createBook(book));
assertThat(bookCreated.getRate()).isNotNull()
.isNotZero()
.isGreaterThan(Double.valueOf(0));
}
@Test
public void itShouldNotFillTheRatingValueFromNonexistentBook() throws IOException {
var book = bookFakerFactory.createBookModel();
book.setTitle("blablablablablabla");
var bookCreated = service.store(bookFakerFactory.createBook(book));
assertThat(bookCreated.getRate()).isZero();
}
}
注意Junit中存在的assertThat
方法的使用,该方法允许检查值。
步骤05-集成测试
我们现在可以创建集成测试,该测试将使我们能够测试系统的路线。我们还将检查请求是否正确返回,以及HTTP代码是否符合期望。
在src.main.test.java.com.simplelibrary.simplelibrary.API
软件包中,我们将创建一个名为integration
的软件包,其中一个名为BookControllerTest
的类:
package com.simplelibrary.simplelibraryAPI.integration;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.simplelibrary.simplelibraryAPI.factory.BookFakerFactory;
import com.simplelibrary.simplelibraryAPI.repository.BookRepository;
import org.junit.jupiter.api.Test;
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.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.assertj.core.api.Assertions.*;
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
public class BookControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private BookRepository repository;
private BookFakerFactory bookFakerFactory;
public BookControllerTest() {
this.bookFakerFactory = new BookFakerFactory();
}
}
请注意使用新注释的使用:@AutoConfigureMockMvc
从中我们将能够使用注射的MockMvc
类向我们的服务器提出请求。
我们可以想到以下测试用例:
- 验证如果一本书成功创建
- 验证如果我们不发送所有必要字段(HTTP代码400) 时未创建书籍
- 验证是否成功列出了所有书籍
- 验证单书的信息是否成功出现。
- 验证书籍的信息已成功更改
- 验证一本书的信息未能在未提交时成功更改。
- 验证书是否成功删除。
- 验证一本不存在的书没有被删除。
所有实施方法的类看起来像这样:
package com.simplelibrary.simplelibraryAPI.integration;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.simplelibrary.simplelibraryAPI.factory.BookFakerFactory;
import com.simplelibrary.simplelibraryAPI.repository.BookRepository;
import org.junit.jupiter.api.Test;
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.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.assertj.core.api.Assertions.*;
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
public class BookControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private BookRepository repository;
private BookFakerFactory bookFakerFactory;
public BookControllerTest() {
this.bookFakerFactory = new BookFakerFactory();
}
@Test
void testIfBookIsCreatedSuccessfully() throws Exception {
var book = this.bookFakerFactory.createBook();
var result = this.mockMvc.perform(post("/books")
.contentType(MediaType.APPLICATION_JSON)
.content(asJsonString(book)))
.andExpect(status().isCreated())
.andReturn();
String content = result.getResponse().getContentAsString();
assertNotNull(content);
}
@Test
void testIfBookIsNotCreatedWithMissingAttributes() throws Exception{
var book = this.bookFakerFactory.createBookIncomplete();
this.mockMvc.perform(post("/books")
.contentType(MediaType.APPLICATION_JSON)
.content(asJsonString(book)))
.andExpect(status().isBadRequest())
.andReturn();
}
@Test
void testIfAllBooksAreListedSuccessfully() throws Exception{
this.mockMvc.perform(get("/books"))
.andExpect(status().isOk())
.andReturn();
}
@Test
void testIfSingleBookInformationIsShown() throws Exception {
this.mockMvc.perform(get("/books"))
.andExpect(status().isOk())
.andReturn();
}
@Test
void testIfBookIsUpdatedSuccessfully() throws Exception{
var book = this.bookFakerFactory.createBookModel();
var bookCreated = repository.save(book);
book.setAuthor("New Author name");
var book2 = this.bookFakerFactory.createBook(book);
this.mockMvc.perform(put("/books/{id}",bookCreated.getId())
.contentType(MediaType.APPLICATION_JSON)
.content(asJsonString(book2)))
.andExpect(status().isOk())
.andReturn();
var bookUpdated = repository.getReferenceById(bookCreated.getId());
assertThat(bookUpdated.getId()).isEqualTo(bookCreated.getId());
assertThat(bookUpdated.getAuthor()).isNotEqualTo(bookCreated.getAuthor());
}
@Test
void testIfBookIsNotUpdatedSuccessfully() throws Exception{
var book = this.bookFakerFactory.createBookModel();
var bookCreated = repository.save(book);
this.mockMvc.perform(put("/books/{id}",bookCreated.getId()))
.andExpect(status().isBadRequest())
.andReturn();
}
@Test
void testIfBookIsDeletedSuccessfully() throws Exception{
var book = this.bookFakerFactory.createBookModel();
var bookCreated = repository.save(book);
this.mockMvc.perform(delete("/books/"+bookCreated.getId()))
.andExpect(status().isNoContent())
.andReturn();
}
@Test
void testIfBookIsNotDeletedSuccessfully() throws Exception{
this.mockMvc.perform(delete("/books/999999"))
.andExpect(status().isNotFound())
.andReturn();
}
public static String asJsonString(final Object obj) {
try {
return new ObjectMapper().writeValueAsString(obj);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
请注意使用asJsonString
方法。它用于采用对象的所有属性值并将其转换为JSON字符串。
步骤06-运行测试
要运行测试,您必须转到终端并键入以下命令:
\.mvnw test
这样做,您将通知Maven您想在应用程序中运行所有测试:
如果所有测试成功进行,您将看到以下内容:
步骤07-测试覆盖范围
最后,很多时候我们进行测试时,我们想了解它的广泛性。我们希望在我们的项目中尽可能多的课程和方法,从而避免任何部分都没有注意到。
我们可以使用 jacoco 插件来检查测试覆盖范围。它的文档存在于at this link。
要使用它,您需要转到pom.xml
文件并添加以下plugin
:
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.8</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
那就是!现在,您可以重新运行测试,完成后,将在target.site.jacoco
中生成.html
文件。该文件为index.html
,访问它时,您的屏幕将与此类似:
您可以看到,com.simplelibrary.simplelibraryAPI.model
包的覆盖率为70%。有关更多信息,您可以单击包裹,然后在所需的类上进行以下覆盖范围视图:
- 绿色表示代码段被称为。
因此,您可以使用插件并添加新测试,以使给定类的覆盖范围尽可能高!
我们在本教程中学到的知识
在第三部分中,我们学习了如何使用Junit执行单元和集成测试。我们还看到了如何使用Jacoco插件检查测试覆盖范围。
在next part中,您将使用Swagger看到有关项目文档的信息。
记住可以找到完整的项目HERE