使用JUnit 对 Spring Boot REST API 执行单元测试

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

使用JUnit 对 Spring Boot REST API 执行单元测试_第1张图片

API 的单元测试 在API测试中时非常重要的一部分,因为单元测试能确保API组件能正常运行。 在这篇文章中我们将学习如何使用JUnit覆盖String Boot Rest API测试。String Boot 是用于创建应用程序的开源框架,也用于创建我们的API。

单元测试API有许多不同的变种和技术。我更喜欢一下组合:Spring Boot, JUnit, MockMvc and Mockito, 因为他们都是开源的并且支持Java,是我的首选语言。

首先,我们必须有 Intellij IDEA 作为开发的IDE, JDK8 作为Java开发。这些都是我个人的喜欢的 但是你也可以使用 Eclipse, NetBeans 甚至是简单的文本编辑器。

现在,我们来建立一个项目,你也可以参与这里的源码 。我们将会测试Controller 和 repository 类。简单的说,我们有4个Controller (ArrivalController, DepartureController, UsersController, FlightsController) 和 4个repository (ArrivalRepository, DepartureRepository, UsersRepository, FlightsRepository). 我们将会为每一个Controller (测试 JSON Object 的大小, 请求的最终状态和对JSON object 里面单个元素的断言)  和 每一个 repository (表里插入2个新的元素并确保返回的结果是相等的) 编写单元测试。

 

第一步 - 创建API 测试项目

1. 安装 IntelliJ IDEA

2. 确保你的 JDK 已经安装 (版本>=1.8.XXX).

Now we will create a new project.

现在我们将创建一个新的项目

3. 打开 IntelliJ 并且点击 “Create New Project”

Increase image使用JUnit 对 Spring Boot REST API 执行单元测试_第2张图片

4. 选择 Gradle, Java 和 JDK 版本

Increase image使用JUnit 对 Spring Boot REST API 执行单元测试_第3张图片

5. 项目的名字

Increase image使用JUnit 对 Spring Boot REST API 执行单元测试_第4张图片

6. 选择项目的存放位置

Increase image使用JUnit 对 Spring Boot REST API 执行单元测试_第5张图片

如果你的所有操作都正确,你现在在编辑中应该可以看到一个空 Java 项目:

Increase image使用JUnit 对 Spring Boot REST API 执行单元测试_第6张图片

第二步 - 添加依赖

现在项目创建好了,我们必须添加依赖。你可以使用这些依赖,因为都是开放的。

具体操作,双击你的build.gradle 文件 增加下面的配置内容到配置文件:

group 'blazemeter'
version '1.0-SNAPSHOT'

buildscript {
   repositories {
       jcenter()
       mavenCentral()
       maven { url "http://repo.spring.io/libs-snapshot" }
   }
   dependencies {
       classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.2.RELEASE")
   }
}

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'org.springframework.boot'

sourceSets {
   main.java.srcDir "src/main/java"
   main.resources.srcDir "src/main/resources"
   test.java.srcDir "src/test/java"
   test.resources.srcDir "src/test/resources"
}

jar {
   baseName = 'blaze-demo-api'
   version =  '1.0'
}

bootRepackage {
   mainClass = 'com.demo.BlazeMeterApi'
}

dependencyManagement {
   imports {
       mavenBom 'io.spring.platform:platform-bom:Brussels-SR2'
   }
}

repositories {
   mavenCentral()
   jcenter()
   maven { url "http://repo.spring.io/libs-snapshot" }
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {

   compile group: 'org.springframework', name: 'spring-core'
   compile group: 'org.springframework.boot', name: 'spring-boot-starter-jdbc'
   compile group: 'org.springframework.boot', name: 'spring-boot-starter-web'
   compile group: 'org.springframework.boot', name: 'spring-boot-starter-actuator'
   compile group: 'org.springframework.boot', name: 'spring-boot-starter-security'
   compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa'
   compile group: 'org.springframework.security.oauth', name: 'spring-security-oauth2'
   compile group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-hibernate4'
   compile group: 'mysql', name: 'mysql-connector-java'
   compile group: 'io.rest-assured', name: 'rest-assured', version: '3.0.3'
   compile group: 'io.rest-assured', name: 'json-schema-validator', version: '3.0.3'

   testCompile group: 'org.springframework.boot', name: 'spring-boot-starter-test'
   testCompile group: 'org.springframework.security', name: 'spring-security-test'
   testCompile group: 'junit', name: 'junit'
   testCompile group: 'org.hsqldb', name: 'hsqldb'
}

第三步 - 使用JUnit 编写单元测试

在 IDEA 中,进入你想要生测试的类,输入快捷键 Cmd + Shift + T (Windows 使用 Ctrl + Shift +T) 会出现一个弹窗,在弹窗里,选择"Create New Test..."。然后,IDEA 将会创建一个用于编写测试的文件。
这个文件将会创建到默认的位置。在我们的事例当中,如果我们要覆盖ArrivalController这个类,将会创建测试类到 test/java/com/demo/controller这个路径。你可以在下面的截图中看到:

Increase image使用JUnit 对 Spring Boot REST API 执行单元测试_第7张图片

我个人比较喜欢分组测试(你可以在相同的图片中看到 - 有3个文件夹:bdd,rest,unit) 根据不同的测试类型:REST,UNIT,BDD,等等 。这样做的原因是,我可以自己来创建我的测试类。在这个事例当中 ArrivalControllerTest 位于 test/java/com/demo/unit/controller 目录下

这里是测试类代码:

package com.demo.unit.controller;

import com.demo.controller.ArrivalController;
import com.demo.domain.Arrival;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import java.util.List;
import static com.demo.constant.Paths.ARRIVAL;
import static com.demo.constant.Paths.VERSION;
import static java.util.Collections.singletonList;
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
import static org.hamcrest.core.Is.is;
import static org.mockito.BDDMockito.given;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@RunWith(SpringRunner.class)
@WebMvcTest(ArrivalController.class)
public class ArrivalControllerTest {

   @Autowired
   private MockMvc mvc;

   @MockBean
   private ArrivalController arrivalController;

   @Test
   public void getArrivals() throws Exception {
       Arrival arrival = new Arrival();
       arrival.setCity("Yerevan");

       List allArrivals = singletonList(arrival);

       given(arrivalController.getAllArrivals()).willReturn(allArrivals);

       mvc.perform(get(VERSION + ARRIVAL + "all")
               .with(user("blaze").password("Q1w2e3r4"))
               .contentType(APPLICATION_JSON))
               .andExpect(status().isOk())
               .andExpect(jsonPath("$", hasSize(1)))
               .andExpect(jsonPath("$[0].city", is(arrival.getCity())));
   }

   @Test
   public void getArrivalsById() throws Exception {
       Arrival arrival = new Arrival();
       arrival.setCity("Yerevan");

       given(arrivalController.getArrivalById(arrival.getId())).willReturn(arrival);

       mvc.perform(get(VERSION + ARRIVAL + arrival.getId())
               .with(user("blaze").password("Q1w2e3r4"))
               .contentType(APPLICATION_JSON))
               .andExpect(status().isOk())
               .andExpect(jsonPath("city", is(arrival.getCity())));
   }
}

在这个测试类中我们有2个测试方法 getArrivals() 和 getArrivalsById()。我们有2个测试方法的原因是在contrller 中有2个方法,因此我们要测试这2个方法。
getArrivals() 这个方法做了以下的事情(上面的代码片段):

  • 创建Arrival实体并设置city的测试值
  • 创建Arrivals集合(因为我们只有一个成员,它就是一个signeltonList)
  • 使用mockito given,确保模拟ArrivalController 将会返回一个Arrivals集合
  • 执行一个带用户凭证的GET 请求 到mocked controller,并且执行简单的断言 
    • 状态 - 200 (which isOk)
    • JSON object 有一个元素
    • JSON body 中有一个我们设置了值的city

在第二个测试方法getArrivalsById()我们做了同样事情。唯一的区别是假定返回一个Arrivals JSON object 代替返回Arrivals JSON object 集合。

到这里。现在我们可以点击方法名前面的按钮(看下图)执行一个单元测试。这个执行的目的是为了确保单元测试能正确的运行:

Increase image使用JUnit 对 Spring Boot REST API 执行单元测试_第8张图片

在执行完成之后你可以看到测试执行的结果,包括状态,计数和堆栈跟踪。我们可以看到我们的测试通过了(在窗体左侧部分),测试的数量(在进度条的顶部)还有执行的堆栈信息

Increase image使用JUnit 对 Spring Boot REST API 执行单元测试_第9张图片

这里只是测试单个 controller。下一步将会为所有controller 添加测试。

第四步 - 为API 创建单元测试

现在,让我们为单个 API repository 类创建单元测试。这个单元测试主要是覆盖数据库测试部分。例如,往DB中写入测试数据,然后验证数据保存的正确性。

像前面一样,进入ArrivalRepository 类,输入快捷键 Cmd + Shift + T 通过IDEA 创建一个新的测试类。
这个代码就是我们的测试类:

package com.demo.unit.repository;

import com.demo.domain.Arrival;
import com.demo.repository.ArrivalRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace.NONE;

@ActiveProfiles("test")
@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = NONE)
public class ArrivalRepositoryTest {

   @Autowired
   private TestEntityManager entityManager;

   @Autowired
   private ArrivalRepository arrivalRepository;

   @Test
   public void whenFindAll() {
       //given
       Arrival firstArrival = new Arrival();
       firstArrival.setCity("Yerevan");
       entityManager.persist(firstArrival);
       entityManager.flush();

       Arrival secondArrival = new Arrival();
       secondArrival.setCity("Israel");
       entityManager.persist(secondArrival);
       entityManager.flush();

       //when
       List arrivals = arrivalRepository.findAll();

       //then
       assertThat(arrivals.size()).isEqualTo(9);
       assertThat(arrivals.get(7)).isEqualTo(firstArrival);
       assertThat(arrivals.get(8)).isEqualTo(secondArrival);
   }

   @Test
   public void whenFindAllById() {
       //given
       Arrival arrival = new Arrival();
       arrival.setCity("Yerevan");
       entityManager.persist(arrival);
       entityManager.flush();

       //when
       Arrival testArrival = arrivalRepository.findAllById(arrival.getId());

       //then
       assertThat(testArrival.getCity()).isEqualTo(arrival.getCity());
   }
}

在这个测试类中,我们测试是使用的H2 数据库。这是比较通常的做法。否则,你必须有一个相似类型的数据库安装在你的 test/dev 环境,你需要维护它们并且在测试完成后确保数据全部清掉。当你使用H2 DB的时候这些操作都不是必须的,因为它是运行在内存当中。测试完成之后,数据库将会全部销毁。

测试涵盖了一下内容:

  • 使用 entityManager 在H2 数据库 Arrival表创建了2条新数据
  • 通过findAll 查询所有的记录
  • Perform assertions on the size of the data and equality of gathered objects
  • 执行断言,对比查询获取的对象数量与实际数据大小

第二个方法做了类似的操作,但因为它是执行的findAllById, 检查的仅仅是单个对象相等。

像这样,你现在可以为其他repository 类创建他们的测试方法。

第五步 - 为所有的API 执行单元测试

所有的controller 和 repository 类单元测试都创建好之后,我们需要执行他。选中高亮的单元测试文件夹 -> 右击 -> 选择 "Run Tests" in 'com.demo.unit'  with Coverage(看下一张图片),这样操作我们可以得到测试代码覆盖报告

 

Increase image使用JUnit 对 Spring Boot REST API 执行单元测试_第10张图片

这些覆盖结果,展示了有多少类和方法被单元测试覆盖。

Increase image使用JUnit 对 Spring Boot REST API 执行单元测试_第11张图片

就这酱紫!你现在知道怎么样运行JUnit单元测试测试 REST API了。你现在想自动化测试你的API吗?Get your API Testing started with BlazeMeter.(广告)

 

原文:https://www.blazemeter.com/blog/spring-boot-rest-api-unit-testing-with-junit

转载于:https://my.oschina.net/wenjinglian/blog/1585835

你可能感兴趣的:(使用JUnit 对 Spring Boot REST API 执行单元测试)