使用JUnit 5进行Spring Boot测试

JUnit 5 (JUnit Jupiter)已经存在了相当长的一段时间,并且配备了许多功能。 但令人意外JUnit 5它不是一个默认的测试库相关,当涉及到春节开机测试入门:它仍然JUnit 4.12 ,在2014年发布了回来,如果你考虑使用JUnit 5对你未来基于Spring启动项目,然后这篇博客文章是给你的。 您将通过针对不同用例的Spring Boot测试示例,了解基于GradleMaven的项目的基本设置。

源代码

可以在Github上找到本文的源代码: https : //github.com/kolorobot/spring-boot-junit5 。

从头开始设置项目

对于项目设置,您将需要JDK 11或更高版本以及Gradle或Maven(取决于您的偏好)。 开始使用Spring Boot的最简单方法是使用https://start.spring.io上的Initializr。 选择的唯一依赖项是Spring Web 。 无论您在生成的项目中使用什么依赖项,始终都包含测试依赖项( Spring Boot Starter Test )。

用Gradle构建

使用Initializr生成的Gradle构建的默认项目文件( gradle.build ):

 plugins { id 'org.springframework.boot' version '2.1.8.RELEASE' id 'io.spring.dependency-management' version '1.0.8.RELEASE' id 'java'  }  group = 'pl.codeleak.samples'  version = '0.0.1-SNAPSHOT'  sourceCompatibility = '11'  repositories { mavenCentral()  }  dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test'  } 

为了增加对JUnit 5支持,我们需要排除旧的JUnit 4依赖性,并包括JUnit 5 (JUnit Jupiter)依赖性:

 dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation( 'org.springframework.boot:spring-boot-starter-test' ) { exclude group: 'junit' , module: 'junit' } testCompile 'org.junit.jupiter:junit-jupiter:5.5.2'  }  test { useJUnitPlatform() testLogging { events "passed" , "skipped" , "failed" }  } 

用Maven构建

使用Initializr生成的Maven构建的默认项目文件( pom.xml ):

 <? xml version = "1.0" encoding = "UTF-8" ?>  < project > < modelVersion >4.0.0</ modelVersion > < parent > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-parent</ artifactId > < version >2.1.8.RELEASE</ version > < relativePath /> <!-- lookup parent from repository --> </ parent > < groupId >pl.codeleak.samples</ groupId > < artifactId >spring-boot-junit5</ artifactId > < version >0.0.1-SNAPSHOT</ version > < name >spring-boot-junit5</ name > < description >Demo project for Spring Boot and JUnit 5</ description > < properties > < project.build.sourceEncoding >UTF-8</ project.build.sourceEncoding > < java.version >11</ java.version > </ properties > < dependencies > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-web</ artifactId > </ dependency > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-test</ artifactId > < scope >test</ scope > </ dependency > </ dependencies > < build > < plugins > < plugin > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-maven-plugin</ artifactId > </ plugin > </ plugins > </ build >  </ project > 

为了增加对JUnit 5支持,我们需要排除旧的JUnit 4依赖性,并包括JUnit 5 (JUnit Jupiter)依赖性:

 < properties > < junit.jupiter.version >5.5.2</ junit.jupiter.version >  </ properties >  < dependencies > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-web</ artifactId > </ dependency > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-test</ artifactId > < scope >test</ scope > < exclusions > < exclusion > < groupId >junit</ groupId > < artifactId >junit</ artifactId > </ exclusion > </ exclusions > </ dependency > < dependency > < groupId >org.junit.jupiter</ groupId > < artifactId >junit-jupiter</ artifactId > < version >${junit.jupiter.version}</ version > < scope >test</ scope > </ dependency >  </ dependencies > 

在测试类中使用JUnit 5

Initializr生成的测试包含自动生成的JUnit 4测试。 要应用JUnit 5我们需要更改导入并将JUnit 4替换为JUnit 5扩展。 我们还可以使类和测试方法包受到保护:

 import org.junit.jupiter.api.Test;  import org.junit.jupiter.api.extension.ExtendWith;  import org.springframework.boot.test.context.SpringBootTest;  import org.springframework.test.context.junit.jupiter.SpringExtension;  @ExtendWith (SpringExtension. class )  @SpringBootTest  class SpringBootJunit5ApplicationTests { @Test void contextLoads() { }  } 

提示:如果您不熟悉JUnit 5,请参阅我有关JUnit 5的其他文章: https : //blog.codeleak.pl/search/label/junit 5

运行测试

我们可以使用Maven Wrapper./mvnw clean testGradle Wrapper./gradlew clean test

源代码

请咨询此提交以获取与项目设置相关的更改。

具有单个REST控制器的示例应用程序

该示例应用程序包含一个具有三个端点的REST控制器:

  • /tasks/{id}
  • /tasks
  • /tasks?title={title}

控制器的每个方法都在内部调用JSONPlaceholder –用于测试和原型制作的假在线REST API。

项目文件的结构如下:

 $ tree src/main/java  src/main/java  └── pl └── codeleak └── samples └── springbootjunit5 ├── SpringBootJunit5Application.java ├── config │  ├── JsonPlaceholderApiConfig.java │  └── JsonPlaceholderApiConfigProperties.java └── todo ├── JsonPlaceholderTaskRepository.java ├── Task.java ├── TaskController.java └── TaskRepository.java 

它还具有以下静态资源:

 $ tree src/main/resources/  src/main/resources/  ├── application.properties  ├── static  │  ├── error  │  │  └── 404 .html  │  └── index.html  └── templates 

TaskController将其工作委托给TaskRepository

 @RestController  class TaskController { private final TaskRepository taskRepository; TaskController(TaskRepository taskRepository) { this .taskRepository = taskRepository; } @GetMapping ( "/tasks/{id}" ) Task findOne( @PathVariable Integer id) { return taskRepository.findOne(id); } @GetMapping ( "/tasks" ) List<Task> findAll() { return taskRepository.findAll(); } @GetMapping (value = "/tasks" , params = "title" ) List<Task> findByTitle(String title) { return taskRepository.findByTitle(title); }  } 

TaskRepositoryJsonPlaceholderTaskRepository实现,该TaskRepository在内部使用RestTemplate来调用JSONPlaceholder( https://jsonplaceholder.typicode.com )端点:

 public class JsonPlaceholderTaskRepository implements TaskRepository { private final RestTemplate restTemplate; private final JsonPlaceholderApiConfigProperties properties; public JsonPlaceholderTaskRepository(RestTemplate restTemplate, JsonPlaceholderApiConfigProperties properties) { this .restTemplate = restTemplate; this .properties = properties; } @Override public Task findOne(Integer id) { return restTemplate .getForObject( "/todos/{id}" , Task. class , id); } // other methods skipped for readability  } 

通过JsonPlaceholderApiConfig配置应用程序,该配置JsonPlaceholderApiConfig使用JsonPlaceholderApiConfigProperties绑定来自application.properties一些明智的属性:

 @Configuration  @EnableConfigurationProperties (JsonPlaceholderApiConfigProperties. class )  public class JsonPlaceholderApiConfig { private final JsonPlaceholderApiConfigProperties properties; public JsonPlaceholderApiConfig(JsonPlaceholderApiConfigProperties properties) { this .properties = properties; } @Bean RestTemplate restTemplate() { return new RestTemplateBuilder() .rootUri(properties.getRootUri()) .build(); } @Bean TaskRepository taskRepository(RestTemplate restTemplate, JsonPlaceholderApiConfigProperties properties) { return new JsonPlaceholderTaskRepository(restTemplate, properties); }  } 

application.properties包含几个与JSONPlaceholder端点配置有关的属性:

 json-placeholder.root-uri=https: //jsonplaceholder.typicode.com  json-placeholder.todo-find-all.sort=id  json-placeholder.todo-find-all.order=desc  json-placeholder.todo-find-all.limit= 20 

在此博客文章中了解有关@ConfigurationProperties更多信息: https : //blog.codeleak.pl/2014/09/using-configurationproperties-in-spring.html

源代码

请咨询此提交以获取与应用程序源代码相关的更改。

创建Spring Boot测试

Spring Boot提供了许多支持测试应用程序的实用程序和注释。

创建测试时可以使用不同的方法。 在下面,您将找到创建Spring Boot测试的最常见情况。

在随机端口上运行Web服务器的Spring Boot测试

 @ExtendWith (SpringExtension. class )  @SpringBootTest (webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)  class TaskControllerIntegrationTest { @LocalServerPort private int port; @Autowired private TestRestTemplate restTemplate; @Test void findsTaskById() { // act var task = restTemplate.getForObject( " http://localhost: " + port + "/tasks/1" , Task. class ); // assert assertThat(task) .extracting(Task::getId, Task::getTitle, Task::isCompleted, Task::getUserId) .containsExactly( 1 , "delectus aut autem" , false , 1 ); }  } 

在具有模拟依赖项的随机端口上运行Web服务器的Spring Boot测试

 @ExtendWith (SpringExtension. class )  @SpringBootTest (webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)  class TaskControllerIntegrationTestWithMockBeanTest { @LocalServerPort private int port; @MockBean private TaskRepository taskRepository; @Autowired private TestRestTemplate restTemplate; @Test void findsTaskById() { // arrange var taskToReturn = new Task(); taskToReturn.setId( 1 ); taskToReturn.setTitle( "delectus aut autem" ); taskToReturn.setCompleted( true ); taskToReturn.setUserId( 1 ); when(taskRepository.findOne( 1 )).thenReturn(taskToReturn); // act var task = restTemplate.getForObject( " http://localhost: " + port + "/tasks/1" , Task. class ); // assert assertThat(task) .extracting(Task::getId, Task::getTitle, Task::isCompleted, Task::getUserId) .containsExactly( 1 , "delectus aut autem" , true , 1 ); }  } 

带有模拟MVC层的Spring Boot测试

 @ExtendWith (SpringExtension. class )  @SpringBootTest  @AutoConfigureMockMvc  class TaskControllerMockMvcTest { @Autowired private MockMvc mockMvc; @Test void findsTaskById() throws Exception { mockMvc.perform(get( "/tasks/1" )) .andDo(print()) .andExpect(status().isOk()) .andExpect(content().json( "{\"id\":1,\"title\":\"delectus aut autem\",\"userId\":1,\"completed\":false}" )); }  } 

具有模拟的MVC层和模拟的依赖项的Spring Boot测试

 @ExtendWith (SpringExtension. class )  @SpringBootTest  @AutoConfigureMockMvc  class TaskControllerMockMvcWithMockBeanTest { @Autowired private MockMvc mockMvc; @MockBean private TaskRepository taskRepository; @Test void findsTaskById() throws Exception { // arrange var taskToReturn = new Task(); taskToReturn.setId( 1 ); taskToReturn.setTitle( "delectus aut autem" ); taskToReturn.setCompleted( true ); taskToReturn.setUserId( 1 ); when(taskRepository.findOne( 1 )).thenReturn(taskToReturn); // act and assert mockMvc.perform(get( "/tasks/1" )) .andDo(print()) .andExpect(status().isOk()) .andExpect(content().json( "{\"id\":1,\"title\":\"delectus aut autem\",\"userId\":1,\"completed\":true}" )); }  } 

带有模拟Web层的Spring Boot测试

 @ExtendWith (SpringExtension. class )  @WebMvcTest  @Import (JsonPlaceholderApiConfig. class )  class TaskControllerWebMvcTest { @Autowired private MockMvc mockMvc; @Test void findsTaskById() throws Exception { mockMvc.perform(get( "/tasks/1" )) .andDo(print()) .andExpect(status().isOk()) .andExpect(content().json( "{\"id\":1,\"title\":\"delectus aut autem\",\"userId\":1,\"completed\":false}" )); }  } 

具有模拟Web层和模拟依赖项的Spring Boot测试

 @ExtendWith (SpringExtension. class )  @WebMvcTest  class TaskControllerWebMvcWithMockBeanTest { @Autowired private MockMvc mockMvc; @MockBean private TaskRepository taskRepository; @Test void findsTaskById() throws Exception { // arrange var taskToReturn = new Task(); taskToReturn.setId( 1 ); taskToReturn.setTitle( "delectus aut autem" ); taskToReturn.setCompleted( true ); taskToReturn.setUserId( 1 ); when(taskRepository.findOne( 1 )).thenReturn(taskToReturn); // act and assert mockMvc.perform(get( "/tasks/1" )) .andDo(print()) .andExpect(status().isOk()) .andExpect(content().json( "{\"id\":1,\"title\":\"delectus aut autem\",\"userId\":1,\"completed\":true}" )); }  } 

运行所有测试

我们可以使用Maven Wrapper./mvnw clean testGradle Wrapper./gradlew clean test运行所有./gradlew clean test

使用Gradle运行测试的结果:

 $ ./gradlew clean test  > Task :test  pl.codeleak.samples.springbootjunit5.SpringBootJunit5ApplicationTests > contextLoads() PASSED  pl.codeleak.samples.springbootjunit5.todo.TaskControllerWebMvcTest > findsTaskById() PASSED  pl.codeleak.samples.springbootjunit5.todo.TaskControllerIntegrationTestWithMockBeanTest > findsTaskById() PASSED  pl.codeleak.samples.springbootjunit5.todo.TaskControllerWebMvcWithMockBeanTest > findsTaskById() PASSED  pl.codeleak.samples.springbootjunit5.todo.TaskControllerIntegrationTest > findsTaskById() PASSED  pl.codeleak.samples.springbootjunit5.todo.TaskControllerMockMvcTest > findsTaskById() PASSED  pl.codeleak.samples.springbootjunit5.todo.TaskControllerMockMvcWithMockBeanTest > findsTaskById() PASSED  BUILD SUCCESSFUL in 7s  5 actionable tasks: 5 executed 

参考文献

  • https://docs.spring.io/spring-boot/docs/2.1.8.RELEASE/reference/html/boot-features-testing.html
  • https://spring.io/guides/gs/testing-web/
  • https://github.com/spring-projects/spring-boot/issues/14736

翻译自: https://www.javacodegeeks.com/2019/09/spring-boot-testing-junit-5.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/340990.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android-ViewPager不能显示

问题 如题 解决 忘了在PageAdapter中的instantiateItem&#xff08;&#xff09;方法中创建了视图后调用 container.add(view)&#xff0c;黑线。。。

AndroidStudio cmakelist找不到问题

这里写自定义目录标题AndroidStudio cmakelist找不到问题原因解决AndroidStudio cmakelist找不到问题 原因 在build.gradle中声明了externalNativeBuild &#xff0c;但CMakeList.txt并不存在 externalNativeBuild { cmake { path “CMakeLists.txt” } } 解决 把external…

Window网络无法查看其它设备的共享文件

项目场景&#xff1a; windows共享文件夹能查看到不能访问 问题描述 window中共享文件夹能查看到但不能访问 原因分析&#xff1a; 未添加对应的组或用户名 解决方案&#xff1a; 重新设置可共享的组或用户名 1. 文件夹-》属性-》共享-》确保已经共享 2. 安全-》组或者用户名…

javafx中css选择器_JavaFX技巧12:在CSS中定义图标

javafx中css选择器当您是像我这样来自Swing的UI开发人员时&#xff0c;您很有可能仍在代码中直接设置图像/图标。 最可能是这样的&#xff1a; import javafx.scene.control.Label; import javafx.scene.image.ImageView;public class MyLabel extends Label {public MyLabel(…

EA问题的JDK14实例

Tagir Valeev最近发布了一条有关即将发布的Java JDK14版本的预览功能的推文&#xff1a; &#xff03;Java14模式匹配将名称隐藏带入了更高的疯狂程度。 在这里&#xff0c;我为FLAG字段添加或删除了final修饰符&#xff0c;该修饰符仅在不可达的if分支中访问。 这实际上改变了…

python去除图像光照不均匀_CVPR 2020 | 从重建质量到感知质量:用于低光照增强的半监督学习方法...

CVPR 2020 | 从重建质量到感知质量&#xff1a;用于低光照增强的半监督学习方法Code: https://github.com/flyywh/CVPR-2020-Semi-Low-Light1背景本篇为大家介绍我们组被2020年IEEE国际计算机视觉与模式识别会议(CVPR 2020)接收的工作《From Fidelity to Perceptual Quality: A…

jpa 手动预编译_编译时检查JPA查询

jpa 手动预编译JPA提供了几种查询数据的方法。 可以根据各种标准&#xff08;例如&#xff0c;使用的语言&#xff08;SQL与JPQL&#xff09;或查询是静态的&#xff08;编译时间&#xff09;还是动态的&#xff08;执行时间&#xff09;&#xff09;对此类替代方案进行分类。 …

多层陶瓷电容器用处_【科普贴】多层陶瓷电容器的制造工序,你知道吗?

本文将向大家介绍多层陶瓷电容器的结构及制造工序。多层陶瓷电容器的基本结构电容器用于储存电荷&#xff0c;其最基本结构如图1所示&#xff0c;在2块电极板中间夹着介电体。图1. 电容器的基本结构电容器的性能指标也取决于能够储存电荷的多少。多层陶瓷电容器为了能够储存更多…

使用ORM提取数据很容易! 是吗?

介绍 几乎任何系统都以某种方式与外部数据存储一起运行。 在大多数情况下&#xff0c;它是一个关系数据库&#xff0c;并且数据获取通常委托给某些ORM实现。 ORM涵盖了很多例程&#xff0c;并带来了一些新的抽象作为回报。 Martin Fowler写了一篇有关ORM的有趣文章 &#xff0…

分段式多级离心泵_离心泵与多级离心泵工作原理

离心泵工作原理&#xff1a;离心泵工作时&#xff0c;液体注满泵壳&#xff0c;叶轮高速旋转&#xff0c;液体在离心力作用下产生高速度&#xff0c;高速液体经过逐渐扩大的泵壳通道&#xff0c;动压头转变为静压头。性能特点&#xff1a;高效节能&#xff1a;泵有高效的水力形…

java8 javafx_JavaFX技巧8:美丽深层

java8 javafx如果您正在开发JavaFX的UI框架&#xff0c;请养成一种习惯&#xff0c;始终将自定义控件拆分为控件类和外观类。 来自Swing自己&#xff0c;这对我来说并不明显。 Swing还使用MVC概念&#xff0c;并将实际的组件呈现委托给UI委托&#xff0c;但是扩展Swing的人们大…

牛客网数据开发题库_数据库刷题—牛客网(21-30)

21.查找所有员工自入职以来的薪水涨幅情况&#xff0c;给出员工编号emp_no以及其对应的薪水涨幅growth&#xff0c;并按照growth进行升序CREATE TABLE employees ( emp_no int(11) NOT NULL, birth_date date NOT NULL, first_name varchar(14) NOT NULL, last_name varchar(16…

弹性堆栈介绍

当您运行对公司至关重要的软件时&#xff0c;您将无法获得仅用于分析一段时间前发生的事情的日志&#xff0c;让客户端告诉您您的应用程序已损坏&#xff0c;而您甚至不知道发生了什么是真实的问题。 解决该问题的方法之一是使用监视和日志记录。 大多数应用程序都将具有日志记…

access统计没有选课的人数_当代大学生发愁求职就业,更发愁“选课”,自主选课变成了负担...

当代大学生除了求职就业&#xff0c;最发愁的就是“选课”。不得不说&#xff0c;随着科技的发展&#xff0c;各行各业都发生了翻天覆地的变化。而在大学里的选课&#xff0c;也因此有了巨大的改变。过去&#xff0c;大学生上课&#xff0c;其实课程都是被安排好的&#xff0c;…

产线数字化软件源码_品质笔记⑥丨卢宇聪:把握数字化趋势,坚定创新发展道路...

6天5夜&#xff0c;跨越3座城市&#xff0c;深度走访7家企业&#xff0c;对话多位企业家……这是一趟开阔视野之旅。我接触了很多之前极少有机会接触的企业&#xff0c;比如做光缆的法尔胜泓晟集团、做节能装备的双良集团、做密封件的天生密封件有限公司等。我以前经常接触的是…

es 安装kopf_Elasticsearch-kopf导览

es 安装kopf当我需要一个插件来显示Elasticsearch的集群状态时&#xff0c;或者需要深入了解通常为经典插件elasticsearch-head所达到的索引时。 由于有很多建议&#xff0c;而且似乎是非官方的继任者&#xff0c;所以我最近更详细地研究了elasticsearch-kopf 。 我喜欢它。 我…

会导致小程序onhide码 手机息屏_小程序onshow事件

问题描述onShow 事件在小程序里面非常重要&#xff0c;场景之多&#xff0c;导致处理起来很复杂。很多业务场景依赖与onShow与onHide事件。比如分享给他人&#xff0c;在群里PK等等。息屏&#xff0c;新页面返回、Home键操作&#xff0c;也会触发onShow事件。以下是官网的说明&…

Spring@主要注释

介绍&#xff1a; 当存在多个相同类型的bean时&#xff0c;使用Spring Primary批注为标记的bean提供更高的优先级。 默认情况下&#xff0c;Spring按类型自动连线。 因此&#xff0c;当Spring尝试自动装配并且有多个相同类型的bean时&#xff0c;我们将获得NoUniqueBeanDefini…

python帮助文档快捷键_Pycharm快捷键手册

AltEnter 自动添加包Ctrlt SVN更新Ctrlk SVN提交Ctrl / 注释(取消注释)选择的行CtrlShiftF 高级查找CtrlEnter 补全Shift Enter 开始新行TAB ShiftTAB 缩进/取消缩进所选择的行Ctrl Alt I 自动缩进行Ctrl Y 删除当前插入符所在的行Ctrl D 复制当前行、或者选择的块Ctrl …

arm 交叉编译找不到so_搭建交叉编译环境并验证

1. 搭建编译环境并验证1.1 实验目的 掌握嵌入式开发环境、交叉编译器的搭建、安装和配置方法 熟悉Linux应用程序的编译、调试方法&#xff0c;能够验证X86平台和ARM平台的差异1.2 实验内容 交叉编译器环境搭建 编写一个典型的Linux应用程序 使用GDB调试Linux程序(PC平台) 用Mak…