Spring Boot使用单元测试

为了岩石Spring Boot 使用单元测试,我们将在本文中使用的应用程序是一个API,它提供了对员工信息的一些基本操作。这是一个典型的分层架构——API调用是从控制器处理到持久层的服务。

1,添加依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <version>1.5.3.RELEASE</version>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>test</scope>
    <version>1.4.194</version>
</dependency>

H2 DB是一个内存数据,为了方便测试,省去对应的配置。

2,集成测试使用@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
}

数据访问层使用Spring Data JPA,持久化层的代码:

@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {

    public Employee findByName(String name);

}

测试类:

@RunWith(SpringRunner.class)
@DataJpaTest
public class EmployeeRepositoryTest {

    @Autowired
    private TestEntityManager entityManager;

    @Autowired
    private EmployeeRepository employeeRepository;

    // write test cases here
}

@RunWith(SpringRunner.class)是用于spring boot test 和JUnit的连接桥梁,在使用单元测试时此注解时必须加的。
@DataJpaTest为测试持久化层提供一系列标准的设置:

  • 配置内存数据库
  • 配置 Hibernate,Spring Data 和数据源
  • 支持@EntityScan
  • 打开SQL日志

    The TestEntityManager提供了和标准JPA类似的操作数据的api方法,例如:

    @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());
    }
    
3,使用@MockBean

我们的service层代码是依赖持久化层的,service层代码:

@Service
public class EmployeeServiceImpl implements EmployeeService {

    @Autowired
    private EmployeeRepository employeeRepository;

    @Override
    public Employee getEmployeeByName(String name) {
        return employeeRepository.findByName(name);
    }
}

在某些情况下,我们需要模拟程序的业务逻辑,而不需要通过持久层去操作库,此时我们就可以使用Spring Boot Test提供的Mock来做:

@RunWith(SpringRunner.class)
public class EmployeeServiceImplTest {

    @TestConfiguration
    static class EmployeeServiceImplTestContextConfiguration {

        @Bean
        public EmployeeService employeeService() {
            return new EmployeeServiceImpl();
        }
    }

    @Autowired
    private EmployeeService employeeService;

    @MockBean
    private EmployeeRepository employeeRepository;

    // write test cases here
}

为了实例化一个bean方便在测试类中使用@Autowired注入,可以使用@TestConfiguration来实现。
接下来,通过mock的方式来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);
 }
4,使用@WebMvcTest单元测试

Controller:

@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 EmployeeRestControllerTest {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private EmployeeService service;

    // write test cases here
}

在大多数情况下,@ WebMVC测试将仅限于引导单个控制器。它与 @MockBean一起用于提供所需依赖关系的模拟实现。@WebMvcTest还自动配置MockMvc,它提供了在不启动完整的HTTP服务器的情况下轻松测试MVC控制器的强大方法。

@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())));
}
5,使用@SpringBootTest集成测试

集成测试,故名思议,它是侧重集成应用程序的不同层,在实际开发中,我们应该将集成测试与单元测试分开,不应该与单元测试放到一起运行,我们可以通过使用不同的配置文件来做测试。

@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的webEnvironment属性用来配置运行时环境;我们在这里使用Web..MOCK——这样容器将在模拟servlet环境中操作。
@ TestFrasyStand注释用来配置特定于我们测试的属性文件的位置。请注意,加载了@ TestPropertySource 源的属性文件将重写现有的Apvest.Frasic文件。

application-integrationtest.properties:

spring.datasource.url = jdbc:h2:mem:test
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect

如果你测试mysql,只需要修改次配置文件即可,如下测试类:

@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")));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值