从零入门项目集成Karate和Jacoco,配置测试代码覆盖率
解决问题
在SpringBoot项目中,如何集成Karate测试框架和Jacoco插件。以及编写了feature测试文件,怎么样配置才能看到被测试接口代码的覆盖率。
演示版本及说明
本次讲解,基于SpringBoot2.1.4.RELEASE
版本,可根据项目版本灵活更改。下面所有的版本号,可以自行选择,也可以直接使用下文版本。包括项目目录,都可以自行创建。
1、集成Karate测试框架,及通用配置包
在SpringBoot项目的pom.xml中,添加以下配置:
<dependencies>
<!-- 引入 Web 功能 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>com.intuit.karate</groupId>
<artifactId>karate-junit4</artifactId>
<version>1.3.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.masterthought</groupId>
<artifactId>cucumber-reporting</artifactId>
<version>5.3.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.7</version>
</dependency>
</dependencies>
2、集成Jacoco插件及配置生成接口覆盖率文件
在pom.xml文件中添加:
<build>
<testResources>
<testResource>
<directory>src/test/java</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</testResource>
</testResources>
</build>
<profiles>
<profile>
<id>coverage</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<includes>
<!-- ** 这个很重要 指定为下面创建的入口启动类 -->
<include>demo/DemoTestParallel.java</include>
</includes>
<!-- 这里报红不用管 -->
<argLine>-Dfile.encoding=UTF-8 ${argLine}</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.9</version>
<executions>
<execution>
<id>default-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>default-report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
3、创建启动测试和配置的相关类
1、创建基础启动类
下面的步骤是示例使用,具体使用的时候结合实际创建。
在src/test/java
中创建test
包,在其中创建下面四个类:
1.1 ServerStart
类:
public class ServerStart {
private static final Logger logger = LoggerFactory.getLogger(ServerStart.class);
private ConfigurableApplicationContext context;
private MonitorThread monitor;
private int port = 0;
public void start(String[] args, boolean wait) throws Exception {
if (wait) {
try {
logger.info("attempting to stop server if it is already running");
new ServerStop().stopServer();
} catch (Exception e) {
logger.info("failed to stop server (was probably not up): {}", e.getMessage());
}
}
// Application 改为自己项目的启动类
context = Application.run(args);
ServerStartedInitializingBean ss = context.getBean(ServerStartedInitializingBean.class);
port = ss.getLocalPort();
logger.info("started server on port: {}", port);
if (wait) {
int stopPort = port + 1;
logger.info("will use stop port as {}", stopPort);
monitor = new MonitorThread(stopPort, () -> context.close());
monitor.start();
monitor.join();
}
}
public int getPort() {
return port;
}
@Test
public void startServer() throws Exception {
start(new String[]{}, true);
}
}
1.2 ServerStop
类:
public class ServerStop {
@Test
public void stopServer() {
MonitorThread.stop(8081);
}
}
1.3 MonitorThread
类:
public class MonitorThread extends Thread {
private static final Logger logger = LoggerFactory.getLogger(MonitorThread.class);
private Stoppable stoppable;
private ServerSocket socket;
public MonitorThread(int port, Stoppable stoppable) {
this.stoppable = stoppable;
setDaemon(true);
setName("stop-monitor-" + port);
try {
socket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void run() {
logger.info("starting thread: {}", getName());
Socket accept;
try {
accept = socket.accept();
BufferedReader reader = new BufferedReader(new InputStreamReader(accept.getInputStream()));
reader.readLine();
logger.info("shutting down thread: {}", getName());
stoppable.stop();
accept.close();
socket.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void stop(int port) {
try {
Socket s = new Socket(InetAddress.getByName("127.0.0.1"), port);
OutputStream out = s.getOutputStream();
logger.info("sending stop request to monitor thread on port: {}", port);
out.write(("\r\n").getBytes());
out.flush();
s.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
1.4 Stoppable
接口:
public interface Stoppable {
void stop() throws Exception;
}
2、创建测试启动配置类
在项目的src/test/java
下建一个demo
的包,在包中创建下面两个类:
2.1 TestBase
类:
@RunWith(Karate.class)
public abstract class TestBase {
private static ServerStart server;
public static int startServer() throws Exception {
if (server == null) { // keep spring boot side alive for all tests including package 'mock'
server = new ServerStart();
server.start(new String[]{"--server.port=0"}, false);
}
System.setProperty("demo.server.port", server.getPort() + "");
return server.getPort();
}
@BeforeClass
public static void beforeClass() throws Exception {
startServer();
}
}
2.2 DemoTestParallel
类:
// 该类是测试启动类,也是配置类,需要将该类配置在pom文件里
public class DemoTestParallel {
@BeforeClass
public static void beforeClass() throws Exception {
TestBase.beforeClass();
}
@Test
public void testParallel() {
// 配置想要测试的feature文件所在目录,可自行更改
Results results = Runner.path("classpath:demo")
.outputCucumberJson(true)
// 配置测试环境,根据实际修改,也可以不改
.karateEnv("demo")
.parallel(5);
generateReport(results.getReportDir());
assertTrue(results.getErrorMessages(), results.getFailCount() == 0);
}
public static void generateReport(String karateOutputPath) {
Collection<File> jsonFiles = FileUtils.listFiles(new File(karateOutputPath), new String[] {"json"}, true);
List<String> jsonPaths = new ArrayList<>(jsonFiles.size());
jsonFiles.forEach(file -> jsonPaths.add(file.getAbsolutePath()));
Configuration config = new Configuration(new File("target"), "demo");
ReportBuilder reportBuilder = new ReportBuilder(jsonPaths, config);
reportBuilder.generateReports();
}
}
3、创建karate配置文件
在src/test/java
包下,创建karate-config.js
文件,这个文件是为了设置全局配置信息的,可以设置全局uri,port,env等,因为这是示例,所以简单配置一下uri和port。
function fn() {
var port = '8080';
var config = { demoBaseUrl: 'http://127.0.0.1:' + port };
return config;
}
4、创建服务启动初始化类
在src/main/java/com/karate/config
下创建一个初始化类,如果目录没有,自己创建目录。
@Component
public class ServerStartedInitializingBean implements ApplicationRunner, ApplicationListener<WebServerInitializedEvent> {
private static final Logger logger = LoggerFactory.getLogger(ServerStartedInitializingBean.class);
private int localPort;
public int getLocalPort() {
return localPort;
}
@Override
public void run(ApplicationArguments aa) throws Exception {
logger.info("server started with args: {}", Arrays.toString(aa.getSourceArgs()));
}
@Override
public void onApplicationEvent(WebServerInitializedEvent event) {
localPort = event.getWebServer().getPort();
logger.info("after runtime init, local server port: {}", localPort);
}
}
4、测试接口并生成覆盖率文件
4.1、创建测试接口
在src/main/java/com/karae
中创建controller
包,创建下面的类:
@RestController
public class KarateController {
@GetMapping("/search")
public Map<String, String[]> search(HttpServletRequest request) {
Map<String, String[]> parameterMap = request.getParameterMap();
if (parameterMap == null || parameterMap.size() == 0) {
return null;
}
return request.getParameterMap();
}
}
4.2、创建feature测试文件
在src/test/java/demo/karate
包中,创建karate_test.feature
文件
Feature: karate test controller
Background:
* url demoBaseUrl
Scenario: karate test
# create a test
Given path 'search'
And params ({ name: 'Scooby' })
When method get
Then status 200
And match response == { "name": ["Scooby"] }
5、验证文件是否缺少
下面的图片是演示的项目,可以对比一下,有没有类或文件没有创建。
6、启动命令
历尽千辛万苦,终于到了要验收成果的时候,祝大家好运!
在控制台中输入mvn clean test -Pcoverage
。如果使用的是idea
直接在软件左下方的Terminal
中或者右边maven
配置中输入即可。其他软件,自行找到控制台,目录定位到项目下,执行命令即可。命令执行完,在target
目录下,会有一个site
的文件夹,打开找到index.html
,到浏览器运行即可看到代码覆盖率。
看一下最终的效果截图:
7、总结
在网上找了很久都没有很好的解答,所以自己摸索并记录下来,也希望能帮到更多的人,一起进步!
本次演示,完成了SpringBoot集成Karate测试框架和Jacoco插件。从零开始,一步步实现了对接口代码的测试,以及最终生成被测试代码的覆盖率。演示基于Karate的官网教程,还是比较规范的引入。
后续会继续写一些Karate的语法和场景示例,解决实际项目中测试遇到的问题,例如如何模拟数据库方法或者调用其他服务器接口的方法。
从零入门项目集成Karate和Jacoco,配置测试代码覆盖率的更多相关文章
- [转帖]spring boot项目集成jacoco
小试牛刀:spring boot项目集成jacoco 2019-03-28 20:14:36 zyq23333 阅读数 509 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议, ...
- Taurus.MVC 微服务框架 入门开发教程:项目集成:1、服务端:注册中心、网关(提供可运行程序下载)。
系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 本系列第一篇:Taurus.MVC V3.0.3 微服务开源框架发布:让.NET 架构在大并发的演进过程更简单 ...
- Taurus.MVC 微服务框架 入门开发教程:项目集成:2、客户端:ASP.NET Core(C#)项目集成:应用中心。
系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 本系列第一篇:Taurus.MVC V3.0.3 微服务开源框架发布:让.NET 架构在大并发的演进过程更简单 ...
- Taurus.MVC 微服务框架 入门开发教程:项目集成:5、统一的日志管理。
系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 本系列第一篇:Taurus.MVC V3.0.3 微服务开源框架发布:让.NET 架构在大并发的演进过程更简单 ...
- Taurus.MVC 微服务框架 入门开发教程:项目集成:4、默认安全认证与自定义安全认证。
系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 本系列第一篇:Taurus.MVC V3.0.3 微服务开源框架发布:让.NET 架构在大并发的演进过程更简单 ...
- Taurus.MVC 微服务框架 入门开发教程:项目集成:3、客户端:其它编程语言项目集成:Java集成应用中心。
系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 开源地址:https://github.com/cyq1162/Taurus.MVC 本系列第一篇:Tauru ...
- Taurus.MVC 微服务框架 入门开发教程:项目集成:6、微服务间的调用方式:Rpc.StartTaskAsync。
系统目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 开源地址:https://github.com/cyq1162/Taurus.MVC 本系列第一篇:Tauru ...
- 大数据入门:Maven项目的创建及相关配置
目录 Maven项目的创建及相关配置 一.Maven的介绍 1.Maven是什么: 2.Maven作用: 3.Maven项目的目录结构: 4.Maven的三点坐标: 5.maven的pom文件: 6. ...
- Spring Boot从零入门1_详述
本文属于原创,转载注明出处,欢迎关注微信小程序`小白AI博客` 微信公众号`小白AI`或者网站 [https://xiaobaiai.net](https://xiaobaiai.net) ![](h ...
- 【CuteJavaScript】Angular6入门项目(1.构建项目和创建路由)
本文目录 一.项目起步 二.编写路由组件 三.编写页面组件 1.编写单一组件 2.模拟数据 3.编写主从组件 四.编写服务 1.为什么需要服务 2.编写服务 五.引入RxJS 1.关于RxJS 2.引 ...
随机推荐
- 13. Fluentd输出插件:in_forward用法详解
in_forward插件通常用于从其他节点接收日志事件,这些节点包括其他Fluentd实例.fluent-cat命令行或者Fluentd客户端程序.这是目前效率最高的日志事件接收方法. in_forw ...
- C++ 中std::的使用
std是一个类(输入输出标准),它包括了cin成员和cout成员,using name space std ;以后才能使用它的成员.#include<iostream.h>中不存在类std ...
- 一文读懂Apache Geode缓存中间件
目录 一.对缓存中间件的诉求 1.1 我们为什么需要缓存中间件 1.2 缓存的分类 1.1.1 弱势缓存 1.1.2 强势缓存 二.什么是Apache Geode 2.1 Apache Geode的架 ...
- resultMap处理字段和属性的映射关系
1.resultMap处理字段和属性的映射关系 若字段名和实体类中的属性名不一致,则可以通过resultMap设置自定义映射 <!-- resultMap:设置自定义映射 属性: id:表示自定 ...
- 基本的Dos命令 在控制台如何进入某一个文件或者进入不同的盘符
基本的Dos命令 Windows+R 打开运行小窗口 cmd 进入 D: 切换盘符 dir 查看当前目录下的所有文件 cd 路径,进入某个文件 cd- 返回上一层 cls 清理屏幕 inconfig ...
- 在vue中_this和this的区别
_this只是一个变量名,this代表父函数,如果在子函数还用this,this的指 向就变成子函数了,_this就是用来存储指向的 普通函数中的this表示调用此函数时的对象,箭头函数里面的this ...
- 知识图谱-生物信息学-医学顶刊论文(Briefings in Bioinformatics-2021):生物信息学中的图表示学习:趋势、方法和应用
4.(2021.6.24)Briefings-生物信息学中的图表示学习:趋势.方法和应用 论文标题: Graph representation learning in bioinformatics: ...
- Windows 环境搭建 PostgreSQL 逻辑复制高可用架构数据库服务
本文主要介绍 Windows 环境下搭建 PostgreSQL 的主从逻辑复制,关于 PostgreSQl 的相关运维文章,网络上大多都是 Linux 环境下的操作,鲜有在 Windows 环境下配置 ...
- Kubeadm部署k8s单点master
Kubeadm部署k8s单点master 1.环境准备: 主机名 IP 说明 宿主机系统 master 10.0.0.17 Kubernetes集群的master节点 CentOS 7.9 node1 ...
- ES6 学习笔记(十)Map的基本用法
1 基本用法 Map类型是键值对的有序列表,而键和值都可以是任意类型.可以看做Python中的字典(Dictionary)类型. 1.1 创建方法 Map本身是一个构造函数,用来生成Map实例,如: ...