解决问题

在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,配置测试代码覆盖率的更多相关文章

  1. [转帖]spring boot项目集成jacoco

    小试牛刀:spring boot项目集成jacoco 2019-03-28 20:14:36 zyq23333 阅读数 509   版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议, ...

  2. Taurus.MVC 微服务框架 入门开发教程:项目集成:1、服务端:注册中心、网关(提供可运行程序下载)。

    系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 本系列第一篇:Taurus.MVC V3.0.3 微服务开源框架发布:让.NET 架构在大并发的演进过程更简单 ...

  3. Taurus.MVC 微服务框架 入门开发教程:项目集成:2、客户端:ASP.NET Core(C#)项目集成:应用中心。

    系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 本系列第一篇:Taurus.MVC V3.0.3 微服务开源框架发布:让.NET 架构在大并发的演进过程更简单 ...

  4. Taurus.MVC 微服务框架 入门开发教程:项目集成:5、统一的日志管理。

    系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 本系列第一篇:Taurus.MVC V3.0.3 微服务开源框架发布:让.NET 架构在大并发的演进过程更简单 ...

  5. Taurus.MVC 微服务框架 入门开发教程:项目集成:4、默认安全认证与自定义安全认证。

    系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 本系列第一篇:Taurus.MVC V3.0.3 微服务开源框架发布:让.NET 架构在大并发的演进过程更简单 ...

  6. Taurus.MVC 微服务框架 入门开发教程:项目集成:3、客户端:其它编程语言项目集成:Java集成应用中心。

    系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 开源地址:https://github.com/cyq1162/Taurus.MVC 本系列第一篇:Tauru ...

  7. Taurus.MVC 微服务框架 入门开发教程:项目集成:6、微服务间的调用方式:Rpc.StartTaskAsync。

    系统目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 开源地址:https://github.com/cyq1162/Taurus.MVC 本系列第一篇:Tauru ...

  8. 大数据入门:Maven项目的创建及相关配置

    目录 Maven项目的创建及相关配置 一.Maven的介绍 1.Maven是什么: 2.Maven作用: 3.Maven项目的目录结构: 4.Maven的三点坐标: 5.maven的pom文件: 6. ...

  9. Spring Boot从零入门1_详述

    本文属于原创,转载注明出处,欢迎关注微信小程序`小白AI博客` 微信公众号`小白AI`或者网站 [https://xiaobaiai.net](https://xiaobaiai.net) ![](h ...

  10. 【CuteJavaScript】Angular6入门项目(1.构建项目和创建路由)

    本文目录 一.项目起步 二.编写路由组件 三.编写页面组件 1.编写单一组件 2.模拟数据 3.编写主从组件 四.编写服务 1.为什么需要服务 2.编写服务 五.引入RxJS 1.关于RxJS 2.引 ...

随机推荐

  1. 在客户端电脑使用 kubectl 远程管理 Kubernetes

    日常工作中,可能需要在自己的笔记本电脑上执行 kubectl 命令以管理远程 Linux 服务器上的 Kubernetes 集群.通过调用 kubernetes API 来实现对 Kubernetes ...

  2. Opengl ES之四边形绘制

    四边形的绘制在Opengl ES是很重要的一项技巧,比如做视频播放器时视频的渲染就需要使用到Opengl ES绘制四边形的相关知识.然而在Opengl ES却没有直接提供 绘制四边形的相关函数,那么如 ...

  3. 关于将Azure云上磁盘导出-使用VirtualBox转换成vmdk格式的方法记录

    在工作中,经常会遇到虚拟磁盘文件格式的转换需求,尤其是在虚拟化迁移及云环境迁移到DC的虚拟化环境中 或者中转处理,如最近笔者遇到一个需要将Azure Cloud上的磁盘导出到VMware中,但Azur ...

  4. 编程架构演化史:远古时代,从打孔卡(Punched Card)开始

    回想读书时记录到书本里的打孔纸带编程,到初学编程接触到的C语言高级编程,再到C++.Java面向对象语言产生:从面向过程系统设计 到面向对象系统设计:从三层结构到MVC.MVP.MVVM:从主机到虚拟 ...

  5. 【算法训练营day4】LeetCode24. 两两交换链表中的结点 LeetCode19. 删除链表的倒数第N个结点 LeetCode面试题 02.07. 链表相交 LeetCode142. 环形链表II

    [算法训练营day4]LeetCode24. 两两交换链表中的结点 LeetCode19. 删除链表的倒数第N个结点 LeetCode面试题 02.07. 链表相交 LeetCode142. 环形链表 ...

  6. SQL语句编写的练习(MySQL)

    SQL语句编写的练习(MySQL) 一.建表 1.学生表(Student) 学号 | 姓名 | 性别 | 出生年月 | 所在班级 create table Student( sno varchar(2 ...

  7. 齐博x1如何取消某个标签的缓存时间

    标签默认会有缓存, 如果你要强制取消缓存时间的话, 可以加上下面的参数 time="-1"如下图所示 标签默认缓存时间是10分钟, 你也可以改成其它时间 比如 time=" ...

  8. 齐博x1页面报数据表错误如何修复

    如果服务器突然重启,很容易导致数据表损坏,那是因为有用户正在读取数据表,但服务器突然中断服务,就会报下类下面的错误. 大家凡是看到类似下面的错误,包含关键字 Table *** is marked a ...

  9. flutter系列之:flutter中可以建索引的栈布局IndexedStack

    目录 简介 IndexedStack简介 IndexedStack的使用 总结 简介 之前我们介绍了一个flutter的栈结构的layout组件叫做Stack,通过Stack我们可以将一些widget ...

  10. 微服务 Zipkin 链路追踪原理(图文详解)

    一个看起来很简单的应用,可能需要数十或数百个服务来支撑,一个请求就要多次服务调用. 当请求变慢.或者不能使用时,我们是不知道是哪个后台服务引起的. 这时,我们使用 Zipkin 就能解决这个问题. 由 ...