Spring Boot中的測試



Spring Boot中的測試

簡介

本篇文章我們將會探討一下怎么在SpringBoot使用測試,Spring Boot有專門的spring-boot-starter-test,通過使用它可以很方便的在Spring Boot進行測試。

本文將從repository,service, controller,app四個層級來詳細描述測試案例。

添加maven依賴

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

我們添加spring-boot-starter-test和com.h2database總共兩個依賴。H2數據庫主要是為了測試方便。

Repository測試

本例中,我們使用JPA,首先創建Entity和Repository:

@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);
 
}

測試JPA,我們需要使用@DataJpaTest:

@RunWith(SpringRunner.class)
@DataJpaTest
public class EmployeeRepositoryIntegrationTest {
 
    @Autowired
    private TestEntityManager entityManager;
 
    @Autowired
    private EmployeeRepository employeeRepository;
 
    // write test cases here
 
}

@RunWith(SpringRunner.class) 是Junit和Spring Boot test聯系的橋梁。

@DataJpaTest為persistence layer的測試提供了如下標准配置:

  • 配置H2作為內存數據庫
  • 配置Hibernate, Spring Data, 和 DataSource
  • 實現@EntityScan
  • 開啟SQL logging

下面是我們的測試代碼:

@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());
}

在測試中,我們使用了TestEntityManager。 TestEntityManager提供了一些通用的對Entity操作的方法。上面的例子中我們使用TestEntityManager向Employee插入了一條數據。

Service測試

在實際的應用程序中,Service通常要使用到Repository。但是在測試中我們可以Mock一個Repository,而不用使用真實的Repository。

先看一下Service:

@Service
public class EmployeeServiceImpl implements EmployeeService {
 
    @Autowired
    private EmployeeRepository employeeRepository;
 
    @Override
    public Employee getEmployeeByName(String name) {
        return employeeRepository.findByName(name);
    }
}

我們再看一下怎么Mock Repository。

@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
}

看下上面的例子,我們首先使用了@TestConfiguration專門用在測試中的配置信息,在@TestConfiguration中,我們實例化了一個EmployeeService Bean,然后在EmployeeServiceImplIntegrationTest自動注入。

我們還是用了@MockBean,用來Mock一個EmployeeRepository。

我們看下Mock的實現:

    @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);
    }

上面的代碼中,我們使用Mockito來Mock要返回的數據,然后在接下來的測試中使用。

測試Controller

和測試Service一樣,Controller使用到了Service:

@RestController
@RequestMapping("/api")
public class EmployeeRestController {
 
    @Autowired
    private EmployeeService employeeService;
 
    @GetMapping("/employees")
    public List<Employee> getAllEmployees() {
        return employeeService.getAllEmployees();
    }
}

但是在測試的時候,我們並不需要使用真實的Service,我們需要Mock它 。

@RunWith(SpringRunner.class)
@WebMvcTest(EmployeeRestController.class)
public class EmployeeControllerIntegrationTest {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private EmployeeService service;

    // write test cases here

為了測試Controller,我們需要使用到@WebMvcTest,他會為Spring MVC 自動配置所需的組件。

通常情況下@WebMvcTest 會和@MockBean一起使用來提供Mock的具體實現。

@WebMvcTest也提供了自動配置的MockMvc,它為測試MVC Controller提供了更加簡單的方式,而不需要啟動完整的HTTP server。

@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())));
}

given(service.getAllEmployees()).willReturn(allEmployees); 這一行代碼提供了mock的輸出。方面后面的測試使用。

@SpringBootTest的集成測試

上面我們講的都是單元測試,這一節我們講一下集成測試。

@RunWith(SpringRunner.class)
@SpringBootTest(
        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
        classes = TestApplication.class)
@AutoConfigureMockMvc
@TestPropertySource(
        locations = "classpath:application-integrationtest.properties")
public class EmployeeAppIntegrationTest {

    @Autowired
    private MockMvc mvc;

    @Autowired
    private EmployeeRepository repository;
}

集成測試需要使用@SpringBootTest,在@SpringBootTest中可以配置webEnvironment,同時如果我們需要自定義測試屬性文件可以使用@TestPropertySource。

下面是具體的測試代碼:

   @After
    public void resetDb() {
        repository.deleteAll();
    }

    @Test
    public void givenEmployees_whenGetEmployees_thenStatus200() throws Exception {
        createTestEmployee("bob");
        createTestEmployee("alex");

        // @formatter:off
        mvc.perform(get("/api/employees").contentType(MediaType.APPLICATION_JSON))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
                .andExpect(jsonPath("$", hasSize(greaterThanOrEqualTo(2))))
                .andExpect(jsonPath("$[0].name", is("bob")))
                .andExpect(jsonPath("$[1].name", is("alex")));
        // @formatter:on
    }

    //

    private void createTestEmployee(String name) {
        Employee emp = new Employee(name);
        repository.saveAndFlush(emp);
    }

本文的例子可以參考https://github.com/ddean2009/learn-springboot2/tree/master/springboot-test

更多教程請參考 flydean的博客


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM