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的博客

Spring Boot中的测试的更多相关文章

  1. Spring Boot 中的测试:JUnit

    官方文档:https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html

  2. spring boot(三):Spring Boot中Redis的使用

    spring boot对常用的数据库支持外,对nosql 数据库也进行了封装自动化. redis介绍 Redis是目前业界使用最广泛的内存数据存储.相比memcached,Redis支持更丰富的数据结 ...

  3. Spring Boot中的事务管理

    原文  http://blog.didispace.com/springboottransactional/ 什么是事务? 我们在开发企业应用时,对于业务人员的一个操作实际是对数据读写的多步操作的结合 ...

  4. Spring Boot中使用Swagger2构建强大的RESTful API文档

    由于Spring Boot能够快速开发.便捷部署等特性,相信有很大一部分Spring Boot的用户会用来构建RESTful API.而我们构建RESTful API的目的通常都是由于多终端的原因,这 ...

  5. springboot(十一):Spring boot中mongodb的使用

    mongodb是最早热门非关系数据库的之一,使用也比较普遍,一般会用做离线数据分析来使用,放到内网的居多.由于很多公司使用了云服务,服务器默认都开放了外网地址,导致前一阵子大批 MongoDB 因配置 ...

  6. springboot(三):Spring boot中Redis的使用

    spring boot对常用的数据库支持外,对nosql 数据库也进行了封装自动化. redis介绍 Redis是目前业界使用最广泛的内存数据存储.相比memcached,Redis支持更丰富的数据结 ...

  7. spring boot项目如何测试,如何部署

    有很多网友会时不时的问我,spring boot项目如何测试,如何部署,在生产中有什么好的部署方案吗?这篇文章就来介绍一下spring boot 如何开发.调试.打包到最后的投产上线. 开发阶段 单元 ...

  8. 在Spring Boot中使用数据缓存

    春节就要到了,在回家之前要赶快把今年欠下的技术债还清.so,今天继续.Spring Boot前面已经预热了n篇博客了,今天我们来继续看如何在Spring Boot中解决数据缓存问题.本篇博客是以初识在 ...

  9. 在Spring Boot中使用数据库事务

    我们在前面已经分别介绍了如何在Spring Boot中使用JPA(初识在Spring Boot中使用JPA)以及如何在Spring Boot中输出REST资源(在Spring Boot中输出REST资 ...

随机推荐

  1. 2019级第一次月赛暨ACM工作室第一次招新赛、补题赛

    A:最简单签到,没有之一 Description 此题简单如题意,就是求最大值 Input 多组输入 每组输入输入一串字符串(包括字母和数字),长度小于500 Output 每行输出字符ASCII值与 ...

  2. 1012 The Best Rank (25 分)

    To evaluate the performance of our first year CS majored students, we consider their grades of three ...

  3. 原生js弹力球

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. 使用systemctl工具

                           使用systemctl工具 8.1问题 本例要求掌握systemctl控制工具的基本操作,完成下列任务: 重启httpd.crond.bluetooth服 ...

  5. MyBatis(五):分页

    本文是按照狂神说的教学视频学习的笔记,强力推荐,教学深入浅出一遍就懂!b站搜索狂神说或点击下面链接 https://space.bilibili.com/95256449?spm_id_from=33 ...

  6. go 编译protobuf

    D:\project\bin\protoc.exe --plugin=protoc-gen-go=%GOPATH%\bin\protoc-gen-go.exe --go_out=. *.proto 编 ...

  7. JQUERY滚动加载

    $(document).height():整个网页的高度$(window).height():浏览器可视窗口的高度$(window).scrollTop():浏览器可视窗口顶端距离网页顶端的高度(垂直 ...

  8. hadoop(四)centos7克隆|静态ip|机器名|映射关系|别名配置(完全分布式准备一)|6

    hadoop完全分布式准备工作 克隆默认基础虚拟机三台102/103/104目标:在win10主机上能连上这三台机器,三台机器之间可以互相ping通,用机器名也可ping通.基础虚拟机:创建了文件op ...

  9. mysql 不能对同一个表进行 update(delete) 和 select 联合操作

    eq:     update a set  a.x = 1  where a.y  in (select a.x from  a); 上边语法是错误的,在对aupdate 时不能再条件中对同一个a表进 ...

  10. Wpf之HandyControls与MaterialDesign混用之DataGrid

    首先在App.Xaml引入相关资源 <Application.Resources> <ResourceDictionary> <ResourceDictionary.Me ...