载说明:原创不易,未经授权,谢绝任何形式的转载
创建一个不会崩溃的应用程序
在现代软件开发中,编写和维护高质量的测试用例已经成为我们日常工作的重要部分。而JavaScript作为全球最流行的编程语言之一,拥有大量的库和框架,能够帮助我们更好地进行测试。
在这篇文章中,我将向大家介绍七个优秀的JavaScript测试库,包括Jest、Sinon、Detox、Cucumber、Stryker、TestDouble和Mockttp。这些库在各自的领域中都有出色的表现,如单元测试、功能测试、模拟、集成测试和突变测试等。通过本文的介绍,我希望你能更深入地了解这些库,找到适合你项目的测试工具。
这是GitHub上星标超过15500的顶级库之一。如果你想在你的项目中进行行为驱动开发(Behavior Driven Development)测试,那么这将是一个非常好的资源。它不依赖于浏览器、DOM或任何JavaScript框架,因此非常适合用于网站、Node.js项目,或者任何能运行JavaScript的地方。你可以点击这里查看这个库。
https://github.com/jasmine/jasmine
使用示例
Jasmine是一个用于JavaScript代码的行为驱动开发(BDD)测试框架。它无需DOM和它可以在任何JavaScript支持的环境中运行,包括Node.js和浏览器。
首先,你需要安装Jasmine。在Node.js环境中,你可以通过npm(Node包管理器)来安装:
npm install --save-dev jasmine
安装完Jasmine后,你可以在你的项目中创建一些测试文件。这些测试文件通常称为"spec"文件,在这些文件中你可以写下测试用例。下面是一个简单的示例:
// myFunction.spec.js
const myFunction=require('./myFunction.js');
describe("myFunction", function() {
it("应该返回 'Hello, World!'", function() {
expect(myFunction()).toEqual('Hello, World!');
});
});
在上述代码中,describe函数定义了一组相关的测试,it函数定义了一个单独的测试。expect函数和toEqual函数一起构成一个测试断言,它们判断myFunction的返回值是否为Hello, World!。
假设我们有如下的被测试函数:
// myFunction.js
function myFunction() {
return 'Hello, World!';
}
module.exports=myFunction;
当你想运行测试时,可以在终端中运行以下命令:
npx jasmine myFunction.spec.js
如果myFunction函数的行为符合我们的预期(也就是返回Hello, World!),那么测试就会通过。如果函数的行为与我们的预期不符,那么测试就会失败,并显示一条描述失败原因的消息。
以上就是对Jasmine库的基本介绍和示例。你可以访问其GitHub页面获取更多的信息和详细的文档。
这是一个独立的库,用于在JavaScript测试中创建测试替身(侦查、桩和模拟)。它通过提供工具来验证函数调用、控制行为等,帮助你编写隔离的测试。它在GitHub上有超过9000颗星标。你可以点击这里查看这个库。
https://github.com/sinonjs/sinon
如果你想对你的移动应用进行测试,这将是一个非常好的资源。高速度的原生移动开发需要我们采用持续集成工作流,这就意味着我们对人工质量保证的依赖需要大大降低。这个库可以在真实设备或模拟器上运行你的移动应用进行测试,就像真正的用户一样与它进行交互。它在GitHub上有超过10000颗星标。你可以点击这里查看这个库。
https://github.com/wix/Detox
使用示例
Detox是一个用于端到端测试React Native和其他原生移动应用的库。与其他库不同,Detox提供了一种方式来自动模拟真实用户的行为并且测试应用在真实设备或模拟器上的表现。
首先,你需要在你的项目中安装Detox和它的命令行工具。在Node.js环境中,你可以使用npm(Node包管理器)来安装:
npm install detox --save-dev
npm install -g detox-cli
然后,你需要在你的项目中配置Detox。在你的package.json文件中,你需要添加一个名为"detox"的新字段:
"detox": {
"configurations": {
"ios.sim.debug": {
"binaryPath": "ios/build/Build/Products/Debug-iphonesimulator/YourApp.app",
"build": "xcodebuild -project ios/YourApp.xcodeproj -scheme YourApp -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build",
"type": "ios.simulator",
"device": {
"type": "iPhone 11"
}
}
}
}
在上述配置中,我们定义了一个测试配置,命名为"ios.sim.debug"。这个配置指定了你的应用在哪里构建、应用的类型以及你想在哪种设备上运行测试。
接下来,你可以编写一些端到端的测试用例。这些测试用例会在你指定的设备上运行你的应用并模拟真实用户的行为。以下是一个简单的示例:
// e2e/firstTest.spec.js
describe('Example', ()=> {
beforeEach(async ()=> {
await device.reloadReactNative();
});
it('should have welcome screen', async ()=> {
await expect(element(by.id('welcome'))).toBeVisible();
});
});
在上述代码中,我们首先调用device.reloadReactNative()来确保每个测试用例开始时应用都是在一个新的状态。然后我们使用expect和toBeVisible来断言欢迎界面是否可见。
当你想运行测试时,你需要先构建你的应用,然后再运行测试:
detox build --configuration ios.sim.debug
detox test --configuration ios.sim.debug
如果你的应用的行为符合我们的预期,那么测试就会通过。如果应用的行为与我们的预期不符,那么测试就会失败,并显示一条描述失败原因的消息。
以上就是对Detox库的基本介绍和示例。你可以访问其GitHub页面获取更多的信息和详细的文档。
Cucumber是一个运行用简单语言编写的自动化测试的工具。因为它们是用简单语言编写的,所以你的团队中的任何人都可以阅读。因为任何人都可以阅读,所以你可以使用它们来帮助提高团队的沟通、协作和信任。这是Cucumber的JavaScript实现。它在GitHub上有超过4500颗星标。你可以点击这里查看这个库。
https://github.com/cucumber/cucumber-js
使用示例
Cucumber是一种行为驱动开发(BDD)的工具,它允许开发者用简洁的、近乎自然语言的文本语句(如英语)来描述应用程序的行为,然后可以将这些语句转换为可执行的测试。
首先,你需要在你的项目中安装Cucumber。在Node.js环境中,你可以使用npm(Node包管理器)来安装:
npm install --save-dev @cucumber/cucumber
接下来,你需要创建一个功能文件(通常以 .feature 结尾)。这个文件使用一种名为Gherkin的语言来描述应用程序的行为。例如,你可能有一个如下的功能文件:
# myFeature.feature
Feature: Saying hello
Scenario: User says hello
Given the user has opened the application
When the user says hello
Then the application should reply with "Hello, User!"
然后,你需要创建一些步骤定义(step definitions)。步骤定义是用JavaScript编写的函数,这些函数会被Cucumber用来执行功能文件中的每一步。例如,你可能有一个如下的步骤定义文件:
// mySteps.js
const { Given, When, Then }=require('@cucumber/cucumber');
let appOpen=false;
let saidHello=false;
Given('the user has opened the application', function () {
appOpen=true;
});
When('the user says hello', function () {
if (appOpen) saidHello=true;
});
Then('the application should reply with "Hello, User!"', function () {
if (appOpen && saidHello) {
console.log('Hello, User!');
}
});
最后,你可以通过Cucumber CLI来运行你的功能文件:
npx cucumber-js myFeature.feature
以上就是对Cucumber库的基本介绍和示例。你可以访问其GitHub页面获取更多的信息和详细的文档。
变异测试会对你的代码进行更改,然后针对更改后的代码运行你的单元测试。预期你的单元测试现在会失败。如果它们没有失败,那可能意味着你的测试并没有足够覆盖到代码。正如你所猜测的,这个库将帮助你在项目中进行变异测试。它在GitHub上有超过2000颗星标。你可以点击这里查看这个库。
https://github.com/stryker-mutator/stryker-js
使用示例
Stryker是一个变异测试框架,可以帮助你提高单元测试的质量。变异测试的工作原理是通过对代码进行小的修改(称为“变异”),然后运行你的单元测试以查看哪些修改没有被测试捕获,这可以帮助揭示代码覆盖率的盲点。
首先,你需要在你的项目中安装Stryker和它需要的插件。在Node.js环境中,你可以使用npm(Node包管理器)来安装:
npm install --save-dev @stryker-mutator/core @stryker-mutator/mocha-runner @stryker-mutator/javascript-mutator
在上面的示例中,我们安装了Stryker的核心库,用于运行Mocha测试的运行器以及JavaScript变异器。
然后,你需要创建一个Stryker配置文件。这个文件名通常为stryker.conf.js,并且应该位于项目的根目录下。在这个文件中,你可以定义Stryker应该如何运行你的测试和创建变异。
// stryker.conf.js
module.exports=function(config){
config.set({
mutator: "javascript",
packageManager: "npm",
reporters: ["clear-text", "progress"],
testRunner: "mocha",
transpilers: [],
coverageAnalysis: "off",
mutate: ["src/**/*.js"],
});
};
在上述代码中,我们告诉Stryker使用JavaScript变异器,使用npm作为包管理器,以及使用Mocha作为测试运行器。我们还告诉Stryker需要变异哪些文件。
现在,你可以运行Stryker来执行变异测试了:
npx stryker run
Stryker会生成一份报告,显示每个变异是否被测试覆盖。如果你的单元测试没有捕获到某个变异,那么你可能需要增加或改进你的测试。
以上就是对Stryker库的基本介绍和示例。你可以访问其GitHub页面获取更多的信息和详细的文档。
你在编写JavaScript测试,并在寻找一个模拟库来替你模拟真实的东西吗?这是一个有自己独特见解的,设计精心的测试替身库。该库旨在适用于Node.js和浏览器解释器。它也是测试框架无关的,所以你可以将它放入使用Jasmine、Mocha、Tape、Jest或我们自己的teenytest的代码库中。它在GitHub上有超过1000颗星标。你可以点击这里查看这个库。
https://github.com/testdouble/testdouble.js
使用示例
TestDouble.js 是一个用于在JavaScript中创建测试替身(test doubles)的库。它的设计原则是让你能够在单元测试中轻松地模拟或伪造(fake)依赖,从而让你能够更好地隔离和控制你的测试环境。
首先,你需要在你的项目中安装TestDouble。在Node.js环境中,你可以使用npm(Node包管理器)来安装:
npm install --save-dev testdouble
接下来,你可以在你的单元测试中使用TestDouble。例如,你可以使用td.function()来创建一个模拟函数:
const td=require('testdouble');
// 创建一个模拟函数
const mockFunction=td.function();
// 使模拟函数在调用时返回特定的值
td.when(mockFunction('hello')).thenReturn('world');
// 现在,当你调用 mockFunction('hello') 时,它将返回 'world'
console.log(mockFunction('hello')); // 输出: 'world'
你也可以使用TestDouble来模拟对象,例如使用td.object()来创建一个模拟对象:
const td=require('testdouble');
// 创建一个模拟对象
const mockObject=td.object(['method1', 'method2']);
// 使模拟对象的方法在调用时返回特定的值
td.when(mockObject.method1()).thenReturn('hello');
// 现在,当你调用 mockObject.method1() 时,它将返回 'hello'
console.log(mockObject.method1()); // 输出: 'hello'
TestDouble.js 还提供了许多其他用于创建和管理测试替身的功能,例如验证函数是否被调用,替换模块等。以上就是对TestDouble库的基本介绍和示例,你可以访问其GitHub页面获取更多的信息和详细的文档。
HTTP测试是最常见且支持最好的用例。这个库让你能够在JavaScript中快速、可靠、在任何地方拦截、转换或测试HTTP请求和响应。你可以在集成测试中使用这个库,作为你的测试套件的一部分来拦截真实的请求,或者你可以使用它来构建自定义的HTTP代理,捕获、检查和/或以任何你喜欢的方式重写HTTP。你可以点击这里查看这个库。
https://github.com/httptoolkit/mockttp
使用示例
Mockttp是一个强大的库,它允许你在JavaScript中拦截、检查和修改HTTP请求和响应。这对于集成测试和调试HTTP通信非常有用。
首先,你需要在你的项目中安装Mockttp。在Node.js环境中,你可以使用npm(Node包管理器)来安装:
npm install --save-dev mockttp
接下来,我们将介绍一些基本的使用方式:
// 引入需要的库
const superagent=require("superagent");
const mockServer=require("mockttp").getLocal();
// 在测试开始前启动Mock服务器,并在测试结束后关闭服务器
beforeEach(()=> mockServer.start(8080));
afterEach(()=> mockServer.stop());
// 模拟请求,并对结果进行断言
it("lets you mock requests, and assert on the results", async ()=> {
// 模拟你的端点
await mockServer.forGet("/mocked-path").thenReply(200, "A mocked response");
// 发送一个请求
const response=await superagent.get("http://localhost:8080/mocked-path");
// 对结果进行断言
expect(response.text).to.equal("A mocked response");
});
以上代码创建了一个Mock服务器,并设置了一个模拟的GET请求。然后,我们发送一个实际的GET请求,并断言返回的响应文本是否等于我们设置的模拟响应。
Mockttp还提供了更多高级特性,例如:
以下是一些更高级的示例:
const superagent=require("superagent");
require('superagent-proxy')(superagent);
const mockServer=require("mockttp").getLocal();
describe("Mockttp", ()=> {
beforeEach(()=> mockServer.start());
afterEach(()=> mockServer.stop());
// 不指定端口,允许并行测试
it("lets you mock without specifying a port, allowing parallel testing", async ()=> {
await mockServer.forGet("/mocked-endpoint").thenReply(200, "Tip top testing");
let response=await superagent.get(mockServer.urlFor("/mocked-endpoint"));
expect(response.text).to.equal("Tip top testing");
});
// 验证mock服务器接收的请求详情
it("lets you verify the request details the mockttp server receives", async ()=> {
const endpointMock=await mockServer.forGet("/mocked-endpoint").thenReply(200, "hmm?");
await superagent.get(mockServer.urlFor("/mocked-endpoint"));
const requests=await endpointMock.getSeenRequests();
expect(requests.length).to.equal(1);
expect(requests[0].url).to.equal(`http://localhost:${mockServer.port}/mocked-endpoint`);
});
// 代理请求到任何其他主机
it("lets you proxy requests made to any other hosts", async ()=> {
await mockServer.forGet("http://google.com").thenReply(200, "I can't believe it's not google!");
let response=await superagent.get("http://google.com").proxy(mockServer.url);
expect(response.text).to.equal("I can't believe it's not google!");
});
});
这些示例使用了Mocha,Chai和Superagent,但并非必须使用这些:Mockttp可以与任何可以处理promise的测试工具配合使用,可以模拟来自任何库、工具或设备的请求。
在这篇文章中,我们了解了七个JavaScript测试库:Jest、Sinon、Detox、Cucumber、Stryker、TestDouble和Mockttp。每一个库都有其独特的功能和特点,可以帮助我们更高效地编写和管理测试用例,确保代码的质量和稳定性。
不论你是初学者还是资深开发者,这些库都将是你开发过程中强大的工具。我希望通过本文的介绍,你能更深入地了解这些库,找到最适合你的工具。
在结束本文之前,我想说,测试是软件开发中不可或缺的一部分,选择和掌握合适的测试工具,可以让我们的工作变得更加轻松。最后,希望本文能对你的开发工作带来帮助,如果你有任何问题或者建议,欢迎在评论区留言。感谢阅读,我们下次再见。
由于文章内容篇幅有限,今天的内容就分享到这里,文章结尾,我想提醒您,文章的创作不易,如果您喜欢我的分享,请别忘了点赞和转发,让更多有需要的人看到。同时,如果您想获取更多前端技术的知识,欢迎关注我,您的支持将是我分享最大的动力。我会持续输出更多内容,敬请期待。
文为霍格沃兹测试学院优秀学员关于 Jacoco 的小结和踩坑记录。测试开发进阶学习,文末加群。
测试覆盖率是老生常谈的话题。因为我测试理论基础不是很好,这里就不提需求、覆盖率等内容,直奔主题,本文主要指 Java 后端的测试覆盖率。
由于历史原因,公司基本不做 UT,所以对测试来说,咱最关心的还是手工执行、接口执行 (人工 Postman 之类的)、接口自动化、WebUI 自动化对一个应用系统的覆盖度。
本来 Jacoco 已经流行了很多年了,各种文档和帖子已经描述的很完美了,但是多数文章都是针对某一特定形式做了总结和使用。相信很多负责整个公司项目的覆盖率任务的人们来说,还是要一种一种去研究、去应对,入坑、出坑不厌其烦。
也得益于今年上半年一直负责整个公司不同类型的项目的覆盖率统计技术的适配,对不同形式的项目均有一定的了解,在此记录一下,也不让千疮百孔的自己浪费掉这半年的精力,如果说可以帮到别人一星半点,那这篇文章就算是造福了。由于本人能力有限、表达能力有限,如有错误,还请大家多指正。
因为之前了解过一部分 Jacoco 的机制,也知道它提供了很多强大的功能,以满足不同形式的项目。但归根结底,Jacoco 提供了 API,可以让大家屏蔽不同类型的项目带来的困扰。
Jacoco 官方的 Api 示例地址:
https://www.Jacoco.org/Jacoco/trunk/doc/api.html
个人认为,以 Api 的方式来进行操作,可以有以下好处:
可以屏蔽不同方式的构建部署。如果你想把这个功能做成平台,那 API 想必是很好的一种方式。
也就是说,我只需要把 Jacoco 插桩到测试服务器上,暴露 TCP 的 IP 和端口,剩余的提取代码执行数据、生成覆盖率报告,就可以用统一的方式进行就好了。
众所周知,Jacoco 官方提供了 Maven 插件方式、Ant 的 XML 方式,均有对应的 dump 和 report 来进行覆盖率数据的 dump 和报告生成,大家如果有兴趣可以研究一下,这里不赘述。
由于我所在的公司是个老牌公司,项目杂乱无章,技术五花八门。至今仍然有跑在 JDK6 上的。所以我个人认为,影响 Jacoco 使用过程的,可能存在于以下几点。
我司现有 JDK6、7、8,但实际上 jdk6 是个分水岭,其他的都基本可以用 JDK8 来适配。
我司现有 Maven 构建、ANT 构建,想必有的公司还有用 Gradle 的。
Ant、Maven 插件启动、Java -jar 启动、Tomcat 启动 war 包 (打包方式就随便了)
稍后内容也都基于这几种不同实现方式做描述。如果接触项目多的,基本就知道,很多时候测试还是不介入测试环境的发布,这一方面源于开发的不信任,他们认为发布还是要抓在开发自己手里;另一方面也源于测试人员能力的跟不上,至少在我司很多测试人员确实不太懂如何发布(虽然现在慢慢有所缓解,越来越都的测试人员都从开发手中接了过来)。
线上部署、测试部署、开发部署,这几个不同场景,可能用的方式都不同,至少在我接触的项目大都是这样。开发喜欢用插件的方式启动部署,因为快嘛,而且 IDE 也支持,右键运行一下基本在 IDE 就启动了,想想看如果你是开发,在你本地 IDE 里调试的时候,需要打个 war 包然后丢到 Tomcat 里,再启动 Tomcat,你也不太乐意。
废话不多说,步入正题。Jacoco 介入部署过程的本质,就是插桩,至于怎么插桩,跟接入阶段有关系。可以是编译时插桩、也可以是运行时插桩,这就是所谓 Offline 模式和 On-the-fly 模式,我们也不过多于纠结,我们选择了 on-the-fly 模式。
所以归结到本质,Jacoco 的 on-the-fly 模式的插桩过程,其实就是在测试环境部署的时候,让 Jacoco 的相关工具,介入部署过程,也就是介入 class 文件的加载,在加载 class 的时候,动态改变字节码结构,插入 Jacoco 的探针。
本质:Jacoco 以 TCPserver 方式进行插桩的本质,就是如果应用启动过程中,进行了 Jacoco 插桩,且成功了。它会在你当前这个启动服务器中,在一个端口{$port}上,开启一个 TCP 服务,这个 TCP 服务,会一直接收 Jacoco 的执行覆盖率信息并传到这个 TCP 服务上进行保存。
既然是个 TCP 服务,那 Jacoco 也提供了一种以 API 的方式连接到这个 TCP 服务上,进行覆盖率数据的 dump 操作。(细节可能描述的不是很精确,但差不多就是这么个过程。这个 TCP 服务,在你没有关闭应用的时候,是一直开着的,可以随时接受连接)
再本质一点,就是介入下面这个命令的启动过程:
java -jar
那问题就好办了,一种一种来对应起来。
提到介入启动过程,那就免不了提一下一个 jar 包。
Jacocoagent.jar下载地址:
https://www.eclemma.org/Jacoco/
下载后解压文件夹里,目录如下:
这个 Jacocoagent.jar, 就是启动应用时主要用来插桩的 jar 包。
请注意不要写错名称,里面有个很像的 Jacocoant.jar,这个 jar 包是用 ant xml 方式操作 Jacoco 时使用的,不要混淆。
以测试环境部署在 Linux 服务器上为例,如果想在 Windows 上测试也可以,把对应的值改成 Windows 上识别的即可。
假设 Jacocoagent.jar 的存放路径为:/home/admin/Jacoco/Jacocoagent.jar
以下都以 $JacocoJarPath 来替代这个路径,请注意这个路径不是死的,你可以修改。
依然是基于上述的几种不同方式,那我们针对不同形式·做插桩,也就是改变这几种不同形式的底层启动原理,也就是改动不同方式的 java 的启动参数,这对每一种启动方式都不太一样。但是改动 Java 启动参数本质也是一样的,就是在 java -jar 启动的时候,加入 -javaagent 参数。
-javaagent:$JacocoJarPath=includes=*,output=TCPserver,port=2014,address=192.168.110.1"
换成实际的信息为如下,请注意替换真实路径,这一句是需要介入应用启动过程的主要代码,针对每种不同的部署方式,需要加到不同的地方。
-javaagent:/home/admin/Jacoco/Jacocoagent.jar=includes=*,output=TCPserver,port=2014,address=192.168.110.1
JDK5 之后新增的参数,主要用来在运行 jar 包的时候,以一种方式介入字节码加载过程,如有兴趣自行百度。注意后面有个冒号:
需要用来介入 class 文件加载过程的 jar 包,想深入了解的,百度 “插桩” 哈。这是一个 jar 包的绝对路径。
这个代表了,启动时需要进行字节码插桩的包过滤,* 代表所有的 class 文件加载都需要进行插桩。
假如你们公司内部代码都有相同的包前缀 :com.mycompany<你可以写成:
includes=com.mycompany.*
这个地方不用改动,代表以 TCPserver 方式启动应用并进行插桩。
这是 Jacoco 开启的 TCPserver 的端口,请注意这个端口不能被占用。
这是对外开发的 TCPserver 的访问地址。可以配置 127.0.0.1, 也可以配置为实际访问 IP。
配置为 127.0.0.1 的时候,dump 数据只能在这台服务器上进行 dump,就不能通过远程方式 dump 数据。配置为实际的 IP 地址的时候,就可以在任意一台机器上 (前提是 IP 要通,不通都白瞎),通过 Ant XML 或者 API 方式 dump 数据。举个栗子:
我如上配置了 192.168.110.1:2014 作为 Jacoco 的 TCPserver 启动服务,那我可以在任意一台机器上进行数据的 dump,比如在我本机 Windows 上用 API 或者 XML 方式调用 dump。
如果我配置了 127.0.0.1:2014 作为启动服务器,那么我只能在这台测试机上进行 dump,其他的机器都无法连接到这个 TCPserver 进行 dump。
这句内容,如下,格式是固定的,只有括号内的东西方可改变,其它尽量不要动,连空格都不要多:
-javaagent:(/home/admin/Jacoco/Jacocoagent.jar)=includes=(*),output=TCPserver,port=(2014),address=(192.168.110.1)
比如我可以改成其他的:
-javaagent:/home/admin/Jacoco_new/Jacocoagent.jar=includes=com.company.*,output=TCPserver,port=2019,address=192.168.110.111
注意其他地方基本不用改动。
tomcat 的 war 包方式启动,假设 tomcat 路径为: $CATALINA_HOME=/usr/local/apache-tomcat-8.5.20,我们常用的命令存在于: $CATALINA_HOME\bin下,有 startup.sh 和 shutdown.sh(windows 请自觉改为 bat, 后续不再声明),其实这两个只是封装之后的脚本,底层调用的都是 $CATALINA_HOME\bin\catalina.sh(或者 bat),如图源码:
因此,只需要改动 catalina.sh 中的启动参数即可。
前面提到过,主要改动主要是改动 java -jar,tomcat 是通过一个 JAVA_OPTS 参数来控制额外的 java 启动参数的,我们只需要在合适的地方把上面的启动命令追加到 JAVA_OPTS 即可打开 catalina.sh,找到合适的地方修改 JAVA_OPTS 参数:
理论上,任何地方修改 JAVA_OPTS 参数均可,但我们实验过后,在以下位置加入,是一定可以启动成功的,当然您也可以尝试其他位置。
JAVA_OPTS="$JAVA_OPTS -Dorg.apache.catalina.security.SecurityListener.UMASK=`umask`"
源脚本中有这个注释掉的地方,我们在下方修改 JAVA_OPTS,在其下方,加一句:
JAVA_OPTS="$JAVA_OPTS -javaagent:$JacocoJarPath=includes=*,output=TCPserver,port=2014,address=192.168.110.1"
改完之后如下所示:
改完之后,就可以进行 startup.sh 的启动了,应用启动成功之后,可以在服务器上进行调试,查看 TCPserver 是否真的起来了。
判别方式如下 (该图中是现有的已经开启的服务,所以 IP 和端口跟前面的命令不一样,这点请注意,这里只是为了展示;后续几种方式判别方式相同,不再赘述了哈), 这个端口在应用启动时被占用,在应用关闭时被释放,这个请注意检查:
如此,这个端口已经在监听了,证明这个测试环境已经把 Jacoco 注入进去,那你对该测试环境的任何操作,代码执行信息都会被记录到这个 ip:port 开启的 TCP 服务中。
在我司,有的开发会喜欢用插件方式启动,在代码 pom 文件层级中,运行如下命令:
mvn clean install
mvn tomcat7:run -Dport=xxx
或者还有
mvn clean install
mvn spring-boot:run -Dport=xxx
这两套命令,本质上没什么差别,只是运行插件不一样,具体用什么命令,如果不清楚,最好是跟开发请教一下。
他们的意思是,在当前代码的 pom 文件层级运行,意思是通过 maven 的 tomcat 插件启动这个服务,这个服务启动在端口 xxx 上,注意这个端口是应用的访问端口,和 Jacoco 的那个端口不是一回事。
对这种方式注入 Jacoco,也是可以的。这种可以不用修改任何的配置文件,只需要在你启动的时候,临时修改变量就行了。这种方式改变 java 的启动参数方式是这样:
export MAVEN_OPTS="-javaagent:$JacocoJarPath=includes=*,output=TCPserver,port=2014,address=192.168.110.1"
这句命令加在哪里呢?就是 run 之前。为什么呢,因为这样一改,你的所有的 mvn 命令都会生效,但其实我们只想介入启动过程。因此,前面提到的两套启动命令,就可以改成如下方式:
mvn clean install
export MAVEN_OPTS="-javaagent:$JacocoJarPath=includes=*,output=TCPserver,port=2014,address=192.168.110.1"
mvn tomcat7:run -Dport=xxx
export MAVEN_OPTS=""
和
mvn clean install
export MAVEN_OPTS="-javaagent:$JacocoJarPath=includes=*,output=TCPserver,port=2014,address=192.168.110.1"
mvn spring-boot:run -Dport=xxx
export MAVEN_OPTS=""
当然,你的 run 命令,也可能是其他变种,比如:nohup mvn …. & 这种后台启动的方式,也是可以的。最后修改为 "" 是因为担心对后续的 mvn 命令产生影响,其实如果你切换了 terminal 窗口,这个临时变量就会失效,不会对环境造成污染。
如果应用启动成功了,就可以按照前面的方式,netstat 叛别一下 TCP 服务是否真的启动。
如果你设置了这个变量的位置不对,那你用 mvn 命令的时候,可能会出现如下的异常:
java.net.BindException: Address already in use: bind
这时候,就需要去检查一些,你配置的 Jacoco 端口是不是在启动应用服务时已经被占用。或者你临时设置了 MAVEN_OPTS 这个变量,启动之后又没有改回来,然后接着运行了 mvn 命令,这时候也会出现这种错误。这里请务必关注。
提一句题外话,ANT 的方式是不是也可以通过临时修改 ANT_OPTS 参数进行启动 (因为 ANT 和 MAVEN 本是一家子吗,我猜底层可能差异不是很大),我不曾做尝试,有兴趣的可以尝试下。
这种方式可能实现启动应用的阶段不同,但大都配置在 build.xml 里,这里请根据不同的项目做不同的适配。
它的原理是,在 Ant 的启动 target 中,有个 的标签,给她增加一个 jvmarg 参数的子标签,如下代码:
<jvmarg value=”-javaagent:$JacocoJarPath=includes=*,output=TCPserver,port=2014,address=192.168.110.1” />
比如我们的启动命令是这样:
ant -f build.xml clean build startJetty
以此启动之后,将会注入 Jacoco 的代理,最终可以按照上面的方式判断端口是否启动。
这种最简单直接:
java -javaagent: $JacocoJarPath=includes=*,output=TCPserver,port=2014,address=192.168.110.1 -jar xxxxxxxxxx.jar
注意,javaagent 参数,一定要在 jar 包路径之前,尽量在-jar 之前,不然可能不会生效。请注意 java -jar 命令的使用方式,在 jar 包前面传进去的是给 jvm 启动参数的,在 jar 包之后跟的是给 main 方法的。
启动后,依然按照前面的方式判断是否启动了监听端口。
启动之后,就进行测试就可以了,跟平常不注入 Jacoco 代理是无异的。
想继续观看精彩内容可看下文。
(文章来源于霍格沃兹测试学院)
于前端程序员来说,V8引擎无疑是最为熟悉的工具之一了。V8是Google开源的JavaScript和WebAssembly引擎,用C++编写。它用于Chrome和Node.js等。V8可以独立运行,也可以嵌入到任何C++应用程序中。
为了测试V8作为JavaScript引擎的性能,Google随后也开发了一套V8基准测试套件,在运行时,V8基准套件会载入一些特定的JavaScript代码,从而测试引擎的内核、加密、解密、渲染等速度。而该套件也就成为了JavaScript引擎性能的标准。
在该套件的第七个版本中,一共包括了八项基准测试,最终得分为这八项测试得分的几何平均数。得分越高表明速度越快。这八项测试的具体内容如下:
一、Richards基准
操作系统内核的模拟基准, 最早出现于Matin Richards开发的BCPL中(539 行)。
主要关注点:属性加载/存储、函数/方法调用
次要关注点:代码优化、消除冗余代码
二、DeltaBlue基准
单向约束求解,最早出现于 John Maloney 和 Mario Wolczko开发的Smalltalk中 (880 行)。
主要关注点:多态
次要关注点:OO 样式编程
三、Crypto基准
Tom Wu开发的以代码为基础的加密解密基准(1698 行)。
主要关注点:位运算
四、RayTrace基准
Adam Burmister开发的以代码为基础的光线追踪基准 (904 行)。
主要关注点:参数对象,应用
次要关注点:原型库对象,创建模式
五、EarleyBoyer基准
经典Scheme 基准, 由Florian Loitsch的Scheme2Js编译器翻译为JavaScript (4684 行)。
主要关注点:快速创建、销毁对象
次要关注点:闭包, 参数对象
六、RegExp基准
正则表达式基准,从50多个最流行的网页中提取正则表达式操作所产生的(1761 行)。
关注点:正则表达式
七、Splay基准
数据操作基准,处理伸展树和执行自动内存管理子系统 (394 行)。
主要关注点:快速创建、销毁对象
八、NavierStokes基准
根据奥利弗·亨特的代码,在2D上解决navierstokes方程,重操纵双精度数组。(387 行).
主要关注点:读取和写入数字数组。
次要关注点:浮点数学运算。
V8基准测试在早期的JavaScript引擎开发过程中应用广泛,很多JS引擎都使用该基准测试用于评测其性能。常用的JS引擎测试结果如下:
尽管V8基准测试套件非常经典,但是随着技术的发展,Google又推出了新的基准测试套件Octane 1.0和2.0,陆续增加了下列九项测试基准:
pdf.js:在JavaScript中实现了Mozilla的PDF阅读器。它可以测量解码和解释的时间(33,056行)。
主要关注点:数组和类型化数组操作。
次要关注点:数学运算和位运算,以及对未来语言功能(例如 promise)的支持
SplayLatency:Splay 测试侧重于虚拟机的垃圾回收子系统。SplayLatency 对现有 Splay 代码进行频繁测量检查点插桩。检查点之间长时间暂停表示 GC 延迟时间较长。此测试衡量延迟暂停的频率,将它们分类为分桶,并根据低分惩罚频繁地长暂停。
主要关注点:垃圾回收延迟
Mandreel:运行3D Bullet物理引擎,该引擎通过Mandreel将C++移植到JavaScript (277377行)。
主要关注点:模拟
MandreelLatency:与SplayLatency 测试类似,此测试通过频繁的时间测量点对 Mandreel 基准进行插桩。由于 Mandreel 对虚拟机编译器施加压力,因此该测试会提供编译器引入的延迟指示。在测量点之间长时间暂停会降低最终得分。
主要关注点:编译器延迟时间
GB Emulator:全部采用JavaScript模拟便携式控制台的架构,以及运行所需的3D模拟(11,097行)。
主要关注点:模拟
Code loading:测量Javascript引擎在加载了一段大型的Javascript程序后开始解码的速度有多快,一个常见的实例为Social Widget。该测试的源代码来自开源代码库(Closure, jQuery)(1,530行)。
主要关注内容:JavaScript 解析和编译
Box2DWeb:基于流行的2D物理引擎Box2DWeb,最初由Erin Catto编写,现被移植到JavaScript。(560行,9000+ 精简版)
主要关注点:浮点数学运算。
次要关注点:包含 Double 的属性、访问器属性。
Zlib:从 Mozilla Emscripten 套件执行的 zlib asm.js/Emscripten 测试(在工作负载 1 中运行)。代码包含在 eval() 中,它保证我们测量的运行时间包括在所有浏览器上解析和编译(2,585 行)。
主要关注点:代码编译和执行
Typescript:Microsoft&Type 39 TypeScript 编译器是一款复杂的应用。此测试用于衡量 TypeScript 编译本身所需的时间,它代表虚拟机在处理复杂、可调整大小的 JavaScript 应用(25918 行)方面的表现。
主要关注点:运行复杂、繁重的应用
除此之外,常用的JavaScript基准测试工具还有Mozilla发布的Kraken、苹果的JetStream、以及Speedometer和Speed-Battle等。
喜欢本文的话,欢迎关注活在信息时代哦:)
*请认真填写需求信息,我们会在24小时内与您取得联系。