经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring Boot » 查看文章
Spring Boot 单元测试和集成测试实现详解
来源:jb51  时间:2019/9/27 10:47:13  对本文有异议

学习如何使用本教程中提供的工具,并在 Spring Boot 环境中编写单元测试和集成测试。

1. 概览

本文中,我们将了解如何编写单元测试并将其集成在 Spring Boot 环境中。你可在网上找到大量关于这个主题的教程,但很难在一个页面中找到你需要的所有信息。我经常注意到初级开发人员混淆了单元测试和集成测试的概念,特别是在谈到 Spring 生态系统时。我将尝试讲清楚不同注解在不同上下文中的用法。

2. 单元测试 vs. 集成测试

维基百科是这么说单元测试的:

在计算机编程中,单元测试是一种软件测试方法,用以测试源代码的单个单元、一个或多个计算机程序模块的集合以及相关的控制数据、使用过程和操作过程,以确定它们是否适合使用。

集成测试

“集成测试(有时也称集成和测试,缩写为 I&T)是软件测试的一个阶段,在这个阶段中,各个软件模块被组合在一起来进行测试。”

简而言之,当我们在做单元测试时,只是测试了一个代码单元,每次只测试一个方法,不包括与正测试组件相交互的其他所有组件。

另一方面,在集成测试中,我们测试各组件之间的集成。由于单元测试,我们可知这些组件行为与所需一致,但不清楚它们是如何在一起工作的。这就是集成测试的职责。

3. Java 单元测试

所有 Java 开发者都知道 JUnit 是执行单元测试的主要框架。它提供了许多注解来对期望进行断言。

Hamcrest 是一个用于软件测试的附加框架。Hamcrest 允许使用现有的 matcher 类来检查代码中的条件,还允许自定义 matcher 实现。要在 JUnit 中使用 Hamcrest matcher,必须使用 assertThat 语句,后跟一个或多个 matcher。

在这里,你可以看到使用这两种框架的简单测试:

  1. import static org.hamcrest.CoreMatchers.allOf;
  2. import static org.hamcrest.CoreMatchers.anyOf;
  3. import static org.hamcrest.CoreMatchers.both;
  4. import static org.hamcrest.CoreMatchers.containsString;
  5. import static org.hamcrest.CoreMatchers.equalTo;
  6. import static org.hamcrest.CoreMatchers.everyItem;
  7. import static org.hamcrest.CoreMatchers.hasItems;
  8. import static org.hamcrest.CoreMatchers.not;
  9. import static org.hamcrest.CoreMatchers.sameInstance;
  10. import static org.hamcrest.CoreMatchers.startsWith;
  11. import static org.junit.Assert.assertArrayEquals;
  12. import static org.junit.Assert.assertEquals;
  13. import static org.junit.Assert.assertFalse;
  14. import static org.junit.Assert.assertNotNull;
  15. import static org.junit.Assert.assertNotSame;
  16. import static org.junit.Assert.assertNull;
  17. import static org.junit.Assert.assertSame;
  18. import static org.junit.Assert.assertThat;
  19. import static org.junit.Assert.assertTrue;
  20.  
  21. import java.util.Arrays;
  22.  
  23. import org.hamcrest.core.CombinableMatcher;
  24. import org.junit.Test;
  25.  
  26. public class AssertTests {
  27. @Test
  28. public void testAssertArrayEquals() {
  29. byte[] expected = "trial".getBytes();
  30. byte[] actual = "trial".getBytes();
  31. assertArrayEquals("failure - byte arrays not same", expected, actual);
  32. }
  33.  
  34. @Test
  35. public void testAssertEquals() {
  36. assertEquals("failure - strings are not equal", "text", "text");
  37. }
  38.  
  39. @Test
  40. public void testAssertFalse() {
  41. assertFalse("failure - should be false", false);
  42. }
  43.  
  44. @Test
  45. public void testAssertNotNull() {
  46. assertNotNull("should not be null", new Object());
  47. }
  48.  
  49. @Test
  50. public void testAssertNotSame() {
  51. assertNotSame("should not be same Object", new Object(), new Object());
  52. }
  53.  
  54. @Test
  55. public void testAssertNull() {
  56. assertNull("should be null", null);
  57. }
  58.  
  59. @Test
  60. public void testAssertSame() {
  61. Integer aNumber = Integer.valueOf(768);
  62. assertSame("should be same", aNumber, aNumber);
  63. }
  64.  
  65. // JUnit Matchers assertThat
  66. @Test
  67. public void testAssertThatBothContainsString() {
  68. assertThat("albumen", both(containsString("a")).and(containsString("b")));
  69. }
  70.  
  71. @Test
  72. public void testAssertThatHasItems() {
  73. assertThat(Arrays.asList("one", "two", "three"), hasItems("one", "three"));
  74. }
  75.  
  76. @Test
  77. public void testAssertThatEveryItemContainsString() {
  78. assertThat(Arrays.asList(new String[] { "fun", "ban", "net" }), everyItem(containsString("n")));
  79. }
  80.  
  81. // Core Hamcrest Matchers with assertThat
  82. @Test
  83. public void testAssertThatHamcrestCoreMatchers() {
  84. assertThat("good", allOf(equalTo("good"), startsWith("good")));
  85. assertThat("good", not(allOf(equalTo("bad"), equalTo("good"))));
  86. assertThat("good", anyOf(equalTo("bad"), equalTo("good")));
  87. assertThat(7, not(CombinableMatcher.<Integer> either(equalTo(3)).or(equalTo(4))));
  88. assertThat(new Object(), not(sameInstance(new Object())));
  89. }
  90.  
  91. @Test
  92. public void testAssertTrue() {
  93. assertTrue("failure - should be true", true);
  94. }
  95. }

4. 介绍我们的案例

让我们来写一个简单的程序吧。其目的是为漫画提供一个基本的搜索引擎。

4.1. Maven 依赖

首先,需要添加一些依赖到我们的工程中。

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-test</artifactId>
  4. <scope>test</scope>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework.boot</groupId>
  8. <artifactId>spring-boot-starter-web</artifactId>
  9. </dependency>
  10. <dependency>
  11. <groupId>org.projectlombok</groupId>
  12. <artifactId>lombok</artifactId>
  13. <version>1.16.20</version>
  14. <scope>provided</scope>
  15. </dependency>

4.2. 定义 Model

我们的模型非常简单,只有两个类组成:Manga 和 MangaResult

4.2.1. Manga 类

Manga 类表示系统检索到的 Manga 实例。使用 Lombok 来减少样板代码。

  1. package com.mgiglione.model;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Builder;
  4. import lombok.Getter;
  5. import lombok.NoArgsConstructor;
  6. import lombok.Setter;
  7.  
  8. @Getter @Setter @NoArgsConstructor @AllArgsConstructor @Builder
  9. public class Manga {
  10. private String title;
  11. private String description;
  12. private Integer volumes;
  13. private Double score;
  14. }

4.2.2. MangaResult

MangaResult 类是包含了一个 Manga List 的包装类。

  1. package com.mgiglione.model;
  2. import java.util.List;
  3. import lombok.Getter;
  4. import lombok.NoArgsConstructor;
  5. import lombok.Setter;
  6. @Getter @Setter @NoArgsConstructor
  7. public class MangaResult {
  8. private List<Manga> result;
  9. }

4.3. 实现 Service

为实现本 Service,我们将使用由 Jikan Moe 提供的免费 API 接口。

RestTemplate 是用来对 API 进行发起 REST 调用的 Spring 类。

  1. package com.mgiglione.service;
  2.  
  3. import java.util.List;
  4.  
  5. import org.slf4j.Logger;
  6. import org.slf4j.LoggerFactory;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.stereotype.Service;
  9. import org.springframework.web.client.RestTemplate;
  10.  
  11. import com.mgiglione.model.Manga;
  12. import com.mgiglione.model.MangaResult;
  13.  
  14. @Service
  15. public class MangaService {
  16.  
  17. Logger logger = LoggerFactory.getLogger(MangaService.class);
  18. private static final String MANGA_SEARCH_URL="http://api.jikan.moe/search/manga/";
  19. @Autowired
  20. RestTemplate restTemplate;
  21. public List<Manga> getMangasByTitle(String title) {
  22. return restTemplate.getForEntity(MANGA_SEARCH_URL+title, MangaResult.class).getBody().getResult();
  23. }
  24. }

4.4. 实现 Controller

下一步就是写一个暴露了两个端点的 REST Controller,一个是同步的,一个是异步的,其仅用于测试目的。该 Controller 使用了上面定义的 Service。

  1. package com.mgiglione.controller;
  2.  
  3. import java.util.List;
  4. import java.util.concurrent.CompletableFuture;
  5.  
  6. import org.slf4j.Logger;
  7. import org.slf4j.LoggerFactory;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.scheduling.annotation.Async;
  10. import org.springframework.web.bind.annotation.PathVariable;
  11. import org.springframework.web.bind.annotation.RequestMapping;
  12. import org.springframework.web.bind.annotation.RequestMethod;
  13. import org.springframework.web.bind.annotation.ResponseBody;
  14. import org.springframework.web.bind.annotation.RestController;
  15.  
  16. import com.mgiglione.model.Manga;
  17. import com.mgiglione.service.MangaService;
  18.  
  19. @RestController
  20. @RequestMapping(value = "/manga")
  21. public class MangaController {
  22.  
  23. Logger logger = LoggerFactory.getLogger(MangaController.class);
  24. @Autowired
  25. private MangaService mangaService;
  26. @RequestMapping(value = "/async/{title}", method = RequestMethod.GET)
  27. @Async
  28. public CompletableFuture<List<Manga>> searchASync(@PathVariable(name = "title") String title) {
  29. return CompletableFuture.completedFuture(mangaService.getMangasByTitle(title));
  30. }
  31. @RequestMapping(value = "/sync/{title}", method = RequestMethod.GET)
  32. public @ResponseBody <List<Manga>> searchSync(@PathVariable(name = "title") String title) {
  33. return mangaService.getMangasByTitle(title);
  34. }
  35. }

4.5. 启动并测试系统

  1. mvn spring-boot:run

然后,Let's try it:

  1. curl http://localhost:8080/manga/async/ken
  2. curl http://localhost:8080/manga/sync/ken

示例输出:

  1. {
  2. "title":"Rurouni Kenshin: Meiji Kenkaku Romantan",
  3. "description":"Ten years have passed since the end of Bakumatsu, an era of war that saw the uprising of citizens against the Tokugawa shogunate. The revolutionaries wanted to create a time of peace, and a thriving c...",
  4. "volumes":28,
  5. "score":8.69
  6. },
  7. {
  8. "title":"Sun-Ken Rock",
  9. "description":"The story revolves around Ken, a man from an upper-class family that was orphaned young due to his family's involvement with the Yakuza; he became a high school delinquent known for fighting. The only...",
  10. "volumes":25,
  11. "score":8.12
  12. },
  13. {
  14. "title":"Yumekui Kenbun",
  15. "description":"For those who suffer nightmares, help awaits at the Ginseikan Tea House, where patrons can order much more than just Darjeeling. Hiruko is a special kind of a private investigator. He's a dream eater....",
  16. "volumes":9,
  17. "score":7.97
  18. }

5. Spring Boot 应用的单元测试

Spring Boot 提供了一个强大的类以使测试变得简单: @SpringBootTest 注解

可以在基于 Spring Boot 运行的测试类上指定此注解。

除常规 Spring TestContext Framework 之外,其还提供以下功能:

  • 当 @ContextConfiguration (loader=…) 没有特别声明时,使用 SpringBootContextLoader 作为默认 ContextLoader。
  • 在未使用嵌套的 @Configuration 注解,且未显式指定相关类时,自动搜索 @SpringBootConfiguration。
  • 允许使用 Properties 来自定义 Environment 属性。
  • 对不同的 Web 环境模式提供支持,包括启动在已定义或随机端口上的完全运行的 Web 服务器的功能。
  • 注册 TestRestTemplate 和 / 或 WebTestClient Bean,以便在完全运行在 Web 服务器上的 Web 测试中使用。

此处,我们仅有两个组件需要测试:MangaService 和 MangaController

5.1. 对 MangaService 进行单元测试

为了测试 MangaService,我们需要将其与外部组件隔离开来。本例中,只需要一个外部组件:RestTemplate,我们用它来调用远程 API。

我们需要做的是模拟 RestTemplate Bean,并让它始终以固定的给定响应进行响应。Spring Test 结合并扩展了 Mockito 库,通过 @MockBean 注解,我们可以配置模拟 Bean。

  1. package com.mgiglione.service.test.unit;
  2. import static org.mockito.ArgumentMatchers.any;
  3. import static org.mockito.Mockito.when;
  4. import java.io.IOException;
  5. import java.util.List;
  6. import org.junit.Test;
  7. import org.junit.runner.RunWith;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.boot.test.context.SpringBootTest;
  10. import org.springframework.boot.test.mock.mockito.MockBean;
  11. import org.springframework.http.HttpStatus;
  12. import org.springframework.http.ResponseEntity;
  13. import org.springframework.test.context.junit4.SpringRunner;
  14. import org.springframework.web.client.RestTemplate;
  15. import static org.assertj.core.api.Assertions.assertThat;
  16.  
  17. import com.mgiglione.model.Manga;
  18. import com.mgiglione.model.MangaResult;
  19. import com.mgiglione.service.MangaService;
  20. import com.mgiglione.utils.JsonUtils;
  21.  
  22. @RunWith(SpringRunner.class)
  23. @SpringBootTest
  24. public class MangaServiceUnitTest {
  25. @Autowired
  26. private MangaService mangaService;
  27. // MockBean is the annotation provided by Spring that wraps mockito one
  28. // Annotation that can be used to add mocks to a Spring ApplicationContext.
  29. // If any existing single bean of the same type defined in the context will be replaced by the mock, if no existing bean is defined a new one will be added.
  30. @MockBean
  31. private RestTemplate template;
  32. @Test
  33. public void testGetMangasByTitle() throws IOException {
  34. // Parsing mock file
  35. MangaResult mRs = JsonUtils.jsonFile2Object("ken.json", MangaResult.class);
  36. // Mocking remote service
  37. when(template.getForEntity(any(String.class), any(Class.class))).thenReturn(new ResponseEntity(mRs, HttpStatus.OK));
  38. // I search for goku but system will use mocked response containing only ken, so I can check that mock is used.
  39. List<Manga> mangasByTitle = mangaService.getMangasByTitle("goku");
  40. assertThat(mangasByTitle).isNotNull()
  41. .isNotEmpty()
  42. .allMatch(p -> p.getTitle()
  43. .toLowerCase()
  44. .contains("ken"));
  45. }
  46. }

5.2. 对 MangaController 进行单元测试

正如在 MangaService 的单元测试中所做的那样,我们需要隔离组件。在这种情况下,我们需要模拟 MangaService Bean。

然后,我们还有一个问题……Controller 部分是管理 HttpRequest 的系统的一部分,因此我们需要一个系统来模拟这种行为,而非启动完整的 HTTP 服务器。

MockMvc 是执行该操作的 Spring 类。其可以以不同的方式进行设置:

  • 使用 Standalone Context
  • 使用 WebApplication Context
  • 让 Spring 通过在测试类上使用 @SpringBootTest、@AutoConfigureMockMvc 这些注解来加载所有的上下文,以实现自动装配
  • 让 Spring 通过在测试类上使用 @WebMvcTest 注解来加载 Web 层上下文,以实现自动装配
  1. package com.mgiglione.service.test.unit;
  2.  
  3. import static org.hamcrest.Matchers.is;
  4. import static org.mockito.ArgumentMatchers.any;
  5. import static org.mockito.Mockito.when;
  6. import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;
  7. import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
  8. import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
  9. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
  10. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
  11. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  12. import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
  13.  
  14. import java.util.ArrayList;
  15. import java.util.List;
  16.  
  17. import org.junit.Before;
  18. import org.junit.Test;
  19. import org.junit.runner.RunWith;
  20. import org.springframework.beans.factory.annotation.Autowired;
  21. import org.springframework.boot.test.context.SpringBootTest;
  22. import org.springframework.boot.test.mock.mockito.MockBean;
  23. import org.springframework.http.MediaType;
  24. import org.springframework.test.context.junit4.SpringRunner;
  25. import org.springframework.test.web.servlet.MockMvc;
  26. import org.springframework.test.web.servlet.MvcResult;
  27. import org.springframework.web.context.WebApplicationContext;
  28.  
  29. import com.mgiglione.controller.MangaController;
  30. import com.mgiglione.model.Manga;
  31. import com.mgiglione.service.MangaService;
  32.  
  33. @SpringBootTest
  34. @RunWith(SpringRunner.class)
  35. public class MangaControllerUnitTest {
  36.  
  37. MockMvc mockMvc;
  38. @Autowired
  39. protected WebApplicationContext wac;
  40. @Autowired
  41. MangaController mangaController;
  42. @MockBean
  43. MangaService mangaService;
  44. /**
  45. * List of samples mangas
  46. */
  47. private List<Manga> mangas;
  48. @Before
  49. public void setup() throws Exception {
  50. this.mockMvc = standaloneSetup(this.mangaController).build();// Standalone context
  51. // mockMvc = MockMvcBuilders.webAppContextSetup(wac)
  52. // .build();
  53. Manga manga1 = Manga.builder()
  54. .title("Hokuto no ken")
  55. .description("The year is 199X. The Earth has been devastated by nuclear war...")
  56. .build();
  57. Manga manga2 = Manga.builder()
  58. .title("Yumekui Kenbun")
  59. .description("For those who suffer nightmares, help awaits at the Ginseikan Tea House, where patrons can order much more than just Darjeeling. Hiruko is a special kind of a private investigator. He's a dream eater....")
  60. .build();
  61. mangas = new ArrayList<>();
  62. mangas.add(manga1);
  63. mangas.add(manga2);
  64. }
  65. @Test
  66. public void testSearchSync() throws Exception {
  67. // Mocking service
  68. when(mangaService.getMangasByTitle(any(String.class))).thenReturn(mangas);
  69. mockMvc.perform(get("/manga/sync/ken").contentType(MediaType.APPLICATION_JSON))
  70. .andExpect(status().isOk())
  71. .andExpect(jsonPath("$[0].title", is("Hokuto no ken")))
  72. .andExpect(jsonPath("$[1].title", is("Yumekui Kenbun")));
  73. }
  74.  
  75. @Test
  76. public void testSearchASync() throws Exception {
  77. // Mocking service
  78. when(mangaService.getMangasByTitle(any(String.class))).thenReturn(mangas);
  79. MvcResult result = mockMvc.perform(get("/manga/async/ken").contentType(MediaType.APPLICATION_JSON))
  80. .andDo(print())
  81. .andExpect(request().asyncStarted())
  82. .andDo(print())
  83. // .andExpect(status().is2xxSuccessful()).andReturn();
  84. .andReturn();
  85. // result.getRequest().getAsyncContext().setTimeout(10000);
  86. mockMvc.perform(asyncDispatch(result))
  87. .andDo(print())
  88. .andExpect(status().isOk())
  89. .andExpect(jsonPath("$[0].title", is("Hokuto no ken")));
  90. }
  91. }

正如在代码中所看到的那样,选择第一种解决方案是因为其是最轻量的一个,并且我们可以对 Spring 上下文中加载的对象有更好的治理。

在异步测试中,必须首先通过调用服务,然后启动 asyncDispatch 方法来模拟异步行为。

6. Spring Boot 应用的集成测试

对于集成测试,我们希望提供下游通信来检查我们的主要组件。

6.1. 对 MangaService 进行集成测试

这个测试也是非常简单的。我们不需要模拟任何东西,因为我们的目的就是要调用远程 Manga API。

  1. package com.mgiglione.service.test.integration;
  2.  
  3. import static org.assertj.core.api.Assertions.assertThat;
  4.  
  5. import java.util.List;
  6.  
  7. import org.junit.Test;
  8. import org.junit.runner.RunWith;
  9. import org.springframework.beans.factory.annotation.Autowired;
  10. import org.springframework.boot.test.context.SpringBootTest;
  11. import org.springframework.test.context.junit4.SpringRunner;
  12.  
  13. import com.mgiglione.model.Manga;
  14. import com.mgiglione.service.MangaService;
  15.  
  16. @RunWith(SpringRunner.class)
  17. @SpringBootTest
  18. public class MangaServiceIntegrationTest {
  19.  
  20. @Autowired
  21. private MangaService mangaService;
  22. @Test
  23. public void testGetMangasByTitle() {
  24. List<Manga> mangasByTitle = mangaService.getMangasByTitle("ken");
  25. assertThat(mangasByTitle).isNotNull().isNotEmpty();
  26. }
  27. }

6.2. 对 MangaController 进行集成测试

这个测试和单元测试很是相似,但在这个案例中,我们无需再模拟 MangaService。

  1. package com.mgiglione.service.test.integration;
  2. import static org.hamcrest.Matchers.hasItem;
  3. import static org.hamcrest.Matchers.is;
  4. import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;
  5. import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
  6. import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
  7. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
  8. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
  9. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  10. import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
  11.  
  12. import org.junit.Before;
  13. import org.junit.Test;
  14. import org.junit.runner.RunWith;
  15. import org.springframework.beans.factory.annotation.Autowired;
  16. import org.springframework.boot.test.context.SpringBootTest;
  17. import org.springframework.http.MediaType;
  18. import org.springframework.test.context.junit4.SpringRunner;
  19. import org.springframework.test.web.servlet.MockMvc;
  20. import org.springframework.test.web.servlet.MvcResult;
  21. import org.springframework.web.context.WebApplicationContext;
  22.  
  23. import com.mgiglione.controller.MangaController;
  24.  
  25. @SpringBootTest
  26. @RunWith(SpringRunner.class)
  27. public class MangaControllerIntegrationTest {
  28.  
  29. // @Autowired
  30. MockMvc mockMvc;
  31. @Autowired
  32. protected WebApplicationContext wac;
  33. @Autowired
  34. MangaController mangaController;
  35. @Before
  36. public void setup() throws Exception {
  37. this.mockMvc = standaloneSetup(this.mangaController).build();// Standalone context
  38. // mockMvc = MockMvcBuilders.webAppContextSetup(wac)
  39. // .build();
  40. }
  41. @Test
  42. public void testSearchSync() throws Exception {
  43. mockMvc.perform(get("/manga/sync/ken").contentType(MediaType.APPLICATION_JSON))
  44. .andExpect(status().isOk())
  45. .andExpect(jsonPath("$.*.title", hasItem(is("Hokuto no Ken"))));
  46. }
  47. @Test
  48. public void testSearchASync() throws Exception {
  49. MvcResult result = mockMvc.perform(get("/manga/async/ken").contentType(MediaType.APPLICATION_JSON))
  50. .andDo(print())
  51. .andExpect(request().asyncStarted())
  52. .andDo(print())
  53. .andReturn();
  54. mockMvc.perform(asyncDispatch(result))
  55. .andDo(print())
  56. .andExpect(status().isOk())
  57. .andExpect(jsonPath("$.*.title", hasItem(is("Hokuto no Ken"))));
  58. }
  59. }

7. 结论

我们已经了解了在 Spring Boot 环境下单元测试和集成测试的主要不同,了解了像 Hamcrest 这样简化测试编写的框架。当然,也可以在我的 GitHub 仓库 里找到所有代码。

原文:https://dzone.com/articles/unit-and-integration-tests-in-spring-boot-2

作者:Marco Giglione

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持w3xue。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号