Test in SpringBoot

本文介绍了在SpringBoot中进行测试的方法,包括使用@SpringBootTest进行集成测试,使用@WebMvcTest进行单元测试。集成测试涉及真实数据库,启动完整应用上下文,适合测试不同层的集成。单元测试则通常模拟部分依赖,例如使用@MockBean。文章还讨论了如何配置测试环境,如TestPropertySource,以及如何使用@DataJpaTest测试持久层,同时展示了如何利用MockMvc测试控制器。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Test in SpringBoot

maven依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

从SpringBoot2.4开始,Junit5老式驱动已经被SpringBootTest启动依赖移除,如果我们想使用Junit4,我们需要添加以下依赖

<dependency>
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>

使用@SpringBootTest进行进行集成测试

见名知义,集成测试就是侧重于集成应用程序的不同层,这也就意味着不涉及到Mock。

因为集成测试很耗时以及需要一个实际的数据库来执行,所以我们应该尽量将集成测试与单元测试分开,而不是一起运行。我们可以通过使用不同的配置文件来只运行集成测试。

集成测试需要启动一个容器来执行测试用例。

@RunWith(SpringRunner.class)
@SpringBootTest(
  SpringBootTest.WebEnvironment.MOCK,
  classes = Application.class)
@AutoConfigureMockMvc
@TestPropertySource(
  locations = "classpath:application-integrationtest.properties")
public class EmployeeRestControllerIntegrationTest {

    @Autowired
    private MockMvc mvc;

    @Autowired
    private EmployeeRepository repository;

    // write test cases here
}

@SpringBootTest 注解可以通过创建将在测试中使用的ApplicationContext来引导容器。

@SpringBootTest 注解可以配置webEnvironment属性,WebEnvironment.MOCK表示容器可以模拟servlet环境。

TestPropertySource 注解可以设置该测试的配置文件。加载了TestPropertySource的属性文件将覆盖现有的application.properties 文件。配置文件可以写入如数据库等的属性配置。

集成测试的测试用例可能类似于控制层单元测试:

@Test
public void givenEmployees_whenGetEmployees_thenStatus200()
  throws Exception {

    createTestEmployee("bob");

    mvc.perform(get("/api/employees")
      .contentType(MediaType.APPLICATION_JSON))
      .andExpect(status().isOk())
      .andExpect(content()
      .contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
      .andExpect(jsonPath("$[0].name", is("bob")));
}

与控制器层单元测试的不同之处在于,此处不会Mock任何内容,并且将执行端到端方案。

用@SpringBootTest注释的测试将引导完整的应用程序上下文,这意味着我们可以将组件扫描获取的任何bean@Autowire到我们的测试中

@RunWith(SpringRunner.class)
@SpringBootTest
public class EmployeeServiceImplIntegrationTest {

    @Autowired
    private EmployeeService employeeService;

    // class code ...
}

我们可以利用@TestConfiguration来创建一个单独的测试配置类,这个类不会被正常的组件扫描添加成为bean。因此我们需要利用@Import注解来对每个用到它的测试中进行引入,以及使用@Autowired使用。


@TestConfiguration
public class EmployeeServiceImplTestContextConfiguration {
    
    @Bean
    public EmployeeService employeeService() {
        return new EmployeeService() { 
            // implement methods 
        };
    }
}
---------------------------------------------------------------------
@RunWith(SpringRunner.class)
@Import(EmployeeServiceImplTestContextConfiguration.class)
public class EmployeeServiceImplIntegrationTest {

    @Autowired
    private EmployeeService employeeService;

    // remaining class code
}

当我们需要测试Service层时,我们需要用到持久层的repository,但是测试持久层时,我们不应该关心持久层是否被实现了。SpringBootTest为我们提供了Mocking来解决这个问题。

@RunWith(SpringRunner.class)
public class EmployeeServiceImplIntegrationTest {

    @TestConfiguration
    static class EmployeeServiceImplTestContextConfiguration {
 
        @Bean
        public EmployeeService employeeService() {
            return new EmployeeServiceImpl();
        }
    }

    @Autowired
    private EmployeeService employeeService;

    @MockBean
    private EmployeeRepository employeeRepository;

    // write test cases here
}

@MockBean为 EmployeeRepository 创建一个 Mock,可以绕过实际对EmployeeRepository的调用。

@Before
public void setUp() {
    Employee alex = new Employee("alex");

    Mockito.when(employeeRepository.findByName(alex.getName()))
      .thenReturn(alex);
}
@Test
public void whenValidName_thenEmployeeShouldBeFound() {
    String name = "alex";
    Employee found = employeeService.getEmployeeByName(name);
 
     assertThat(found.getName())
      .isEqualTo(name);
 }

当我们为持久层做集成测试的时候。我们需要用到@DataJpaTest

如下:

@Entity
@Table(name = "person")
public class Employee {

   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private Long id;

   @Size(min = 3, max = 20)
   private String name;

   // standard getters and setters, constructors
}
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {

    public Employee findByName(String name);

}
@RunWith(SpringRunner.class)
@DataJpaTest
public class EmployeeRepositoryIntegrationTest {

    @Autowired
    private TestEntityManager entityManager;

    @Autowired
    private EmployeeRepository employeeRepository;

    // write test cases here

}

@RunWith(SpringRunner.class)提供了Spring Boot Test与Junit之间的桥梁,每当我们在Junit测试中使用任何Spring Boot测试功能时,都需要该注解。

@DataJpaTest提供了测试持久层的一些标准设置

  • 配置H2内存储存数据库
  • 设置Hibernate, Spring Data,和数据源
  • 执行@EntityScan
  • 打开SQL日志记录

Spring Boot TestEntityManager 是JPA EntityManager 的标准替代方法,提供了编写测试时常用的方法。可以为我们在数据库中设置一些数据。

@Test
public void whenFindByName_thenReturnEmployee() {
    // given
    Employee alex = new Employee("alex");
    entityManager.persist(alex);
    entityManager.flush();

    // when
    Employee found = employeeRepository.findByName(alex.getName());

    // then
    assertThat(found.getName())
      .isEqualTo(alex.getName());
}

上面的测试中,我们使用测试实体管理器在数据库中插入一个 Employee,并通过按名称查找 API 读取它。

使用@WebMvcTest进行单元测试

@RestController
@RequestMapping("/api")
public class EmployeeRestController {

    @Autowired
    private EmployeeService employeeService;

    @GetMapping("/employees")
    public List<Employee> getAllEmployees() {
        return employeeService.getAllEmployees();
    }
}
@RunWith(SpringRunner.class)
@WebMvcTest(EmployeeRestController.class)
public class EmployeeRestControllerIntegrationTest {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private EmployeeService service;

    // write test cases here
}

为了测试控制器,我们使用@WebMvcTest。它将为我们的单元测试自动配置Spring MVC基础架构

大多数情况下,@WebMvcTest 将被限制为引导单个控制器。我们还可以将其与@MockBean一起使用,为任何所需的依赖项提供模拟实现。

@WebMvcTest还自动配置了MockMvc,它提供了一种强大的方法来轻松测试MVC控制器,而无需启动完整的HTTP服务器。

@Test
public void givenEmployees_whenGetEmployees_thenReturnJsonArray()
  throws Exception {
    
    Employee alex = new Employee("alex");

    List<Employee> allEmployees = Arrays.asList(alex);

    given(service.getAllEmployees()).willReturn(allEmployees);

    mvc.perform(get("/api/employees")
      .contentType(MediaType.APPLICATION_JSON))
      .andExpect(status().isOk())
      .andExpect(jsonPath("$", hasSize(1)))
      .andExpect(jsonPath("$[0].name", is(alex.getName())));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值