載說明:原創(chuàng)不易,未經(jīng)授權(quán),謝絕任何形式的轉(zhuǎn)載
創(chuàng)建一個(gè)不會(huì)崩潰的應(yīng)用程序
在現(xiàn)代軟件開發(fā)中,編寫和維護(hù)高質(zhì)量的測試用例已經(jīng)成為我們?nèi)粘9ぷ鞯闹匾糠帧6鳭avaScript作為全球最流行的編程語言之一,擁有大量的庫和框架,能夠幫助我們更好地進(jìn)行測試。
在這篇文章中,我將向大家介紹七個(gè)優(yōu)秀的JavaScript測試庫,包括Jest、Sinon、Detox、Cucumber、Stryker、TestDouble和Mockttp。這些庫在各自的領(lǐng)域中都有出色的表現(xiàn),如單元測試、功能測試、模擬、集成測試和突變測試等。通過本文的介紹,我希望你能更深入地了解這些庫,找到適合你項(xiàng)目的測試工具。
這是GitHub上星標(biāo)超過15500的頂級(jí)庫之一。如果你想在你的項(xiàng)目中進(jìn)行行為驅(qū)動(dòng)開發(fā)(Behavior Driven Development)測試,那么這將是一個(gè)非常好的資源。它不依賴于瀏覽器、DOM或任何JavaScript框架,因此非常適合用于網(wǎng)站、Node.js項(xiàng)目,或者任何能運(yùn)行JavaScript的地方。你可以點(diǎn)擊這里查看這個(gè)庫。
https://github.com/jasmine/jasmine
使用示例
Jasmine是一個(gè)用于JavaScript代碼的行為驅(qū)動(dòng)開發(fā)(BDD)測試框架。它無需DOM和它可以在任何JavaScript支持的環(huán)境中運(yùn)行,包括Node.js和瀏覽器。
首先,你需要安裝Jasmine。在Node.js環(huán)境中,你可以通過npm(Node包管理器)來安裝:
npm install --save-dev jasmine
安裝完Jasmine后,你可以在你的項(xiàng)目中創(chuàng)建一些測試文件。這些測試文件通常稱為"spec"文件,在這些文件中你可以寫下測試用例。下面是一個(gè)簡單的示例:
// myFunction.spec.js
const myFunction=require('./myFunction.js');
describe("myFunction", function() {
it("應(yīng)該返回 'Hello, World!'", function() {
expect(myFunction()).toEqual('Hello, World!');
});
});
在上述代碼中,describe函數(shù)定義了一組相關(guān)的測試,it函數(shù)定義了一個(gè)單獨(dú)的測試。expect函數(shù)和toEqual函數(shù)一起構(gòu)成一個(gè)測試斷言,它們判斷myFunction的返回值是否為Hello, World!。
假設(shè)我們有如下的被測試函數(shù):
// myFunction.js
function myFunction() {
return 'Hello, World!';
}
module.exports=myFunction;
當(dāng)你想運(yùn)行測試時(shí),可以在終端中運(yùn)行以下命令:
npx jasmine myFunction.spec.js
如果myFunction函數(shù)的行為符合我們的預(yù)期(也就是返回Hello, World!),那么測試就會(huì)通過。如果函數(shù)的行為與我們的預(yù)期不符,那么測試就會(huì)失敗,并顯示一條描述失敗原因的消息。
以上就是對(duì)Jasmine庫的基本介紹和示例。你可以訪問其GitHub頁面獲取更多的信息和詳細(xì)的文檔。
這是一個(gè)獨(dú)立的庫,用于在JavaScript測試中創(chuàng)建測試替身(偵查、樁和模擬)。它通過提供工具來驗(yàn)證函數(shù)調(diào)用、控制行為等,幫助你編寫隔離的測試。它在GitHub上有超過9000顆星標(biāo)。你可以點(diǎn)擊這里查看這個(gè)庫。
https://github.com/sinonjs/sinon
如果你想對(duì)你的移動(dòng)應(yīng)用進(jìn)行測試,這將是一個(gè)非常好的資源。高速度的原生移動(dòng)開發(fā)需要我們采用持續(xù)集成工作流,這就意味著我們對(duì)人工質(zhì)量保證的依賴需要大大降低。這個(gè)庫可以在真實(shí)設(shè)備或模擬器上運(yùn)行你的移動(dòng)應(yīng)用進(jìn)行測試,就像真正的用戶一樣與它進(jìn)行交互。它在GitHub上有超過10000顆星標(biāo)。你可以點(diǎn)擊這里查看這個(gè)庫。
https://github.com/wix/Detox
使用示例
Detox是一個(gè)用于端到端測試React Native和其他原生移動(dòng)應(yīng)用的庫。與其他庫不同,Detox提供了一種方式來自動(dòng)模擬真實(shí)用戶的行為并且測試應(yīng)用在真實(shí)設(shè)備或模擬器上的表現(xiàn)。
首先,你需要在你的項(xiàng)目中安裝Detox和它的命令行工具。在Node.js環(huán)境中,你可以使用npm(Node包管理器)來安裝:
npm install detox --save-dev
npm install -g detox-cli
然后,你需要在你的項(xiàng)目中配置Detox。在你的package.json文件中,你需要添加一個(gè)名為"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"
}
}
}
}
在上述配置中,我們定義了一個(gè)測試配置,命名為"ios.sim.debug"。這個(gè)配置指定了你的應(yīng)用在哪里構(gòu)建、應(yīng)用的類型以及你想在哪種設(shè)備上運(yùn)行測試。
接下來,你可以編寫一些端到端的測試用例。這些測試用例會(huì)在你指定的設(shè)備上運(yùn)行你的應(yīng)用并模擬真實(shí)用戶的行為。以下是一個(gè)簡單的示例:
// e2e/firstTest.spec.js
describe('Example', ()=> {
beforeEach(async ()=> {
await device.reloadReactNative();
});
it('should have welcome screen', async ()=> {
await expect(element(by.id('welcome'))).toBeVisible();
});
});
在上述代碼中,我們首先調(diào)用device.reloadReactNative()來確保每個(gè)測試用例開始時(shí)應(yīng)用都是在一個(gè)新的狀態(tài)。然后我們使用expect和toBeVisible來斷言歡迎界面是否可見。
當(dāng)你想運(yùn)行測試時(shí),你需要先構(gòu)建你的應(yīng)用,然后再運(yùn)行測試:
detox build --configuration ios.sim.debug
detox test --configuration ios.sim.debug
如果你的應(yīng)用的行為符合我們的預(yù)期,那么測試就會(huì)通過。如果應(yīng)用的行為與我們的預(yù)期不符,那么測試就會(huì)失敗,并顯示一條描述失敗原因的消息。
以上就是對(duì)Detox庫的基本介紹和示例。你可以訪問其GitHub頁面獲取更多的信息和詳細(xì)的文檔。
Cucumber是一個(gè)運(yùn)行用簡單語言編寫的自動(dòng)化測試的工具。因?yàn)樗鼈兪怯煤唵握Z言編寫的,所以你的團(tuán)隊(duì)中的任何人都可以閱讀。因?yàn)槿魏稳硕伎梢蚤喿x,所以你可以使用它們來幫助提高團(tuán)隊(duì)的溝通、協(xié)作和信任。這是Cucumber的JavaScript實(shí)現(xiàn)。它在GitHub上有超過4500顆星標(biāo)。你可以點(diǎn)擊這里查看這個(gè)庫。
https://github.com/cucumber/cucumber-js
使用示例
Cucumber是一種行為驅(qū)動(dòng)開發(fā)(BDD)的工具,它允許開發(fā)者用簡潔的、近乎自然語言的文本語句(如英語)來描述應(yīng)用程序的行為,然后可以將這些語句轉(zhuǎn)換為可執(zhí)行的測試。
首先,你需要在你的項(xiàng)目中安裝Cucumber。在Node.js環(huán)境中,你可以使用npm(Node包管理器)來安裝:
npm install --save-dev @cucumber/cucumber
接下來,你需要?jiǎng)?chuàng)建一個(gè)功能文件(通常以 .feature 結(jié)尾)。這個(gè)文件使用一種名為Gherkin的語言來描述應(yīng)用程序的行為。例如,你可能有一個(gè)如下的功能文件:
# 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!"
然后,你需要?jiǎng)?chuàng)建一些步驟定義(step definitions)。步驟定義是用JavaScript編寫的函數(shù),這些函數(shù)會(huì)被Cucumber用來執(zhí)行功能文件中的每一步。例如,你可能有一個(gè)如下的步驟定義文件:
// 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來運(yùn)行你的功能文件:
npx cucumber-js myFeature.feature
以上就是對(duì)Cucumber庫的基本介紹和示例。你可以訪問其GitHub頁面獲取更多的信息和詳細(xì)的文檔。
變異測試會(huì)對(duì)你的代碼進(jìn)行更改,然后針對(duì)更改后的代碼運(yùn)行你的單元測試。預(yù)期你的單元測試現(xiàn)在會(huì)失敗。如果它們沒有失敗,那可能意味著你的測試并沒有足夠覆蓋到代碼。正如你所猜測的,這個(gè)庫將幫助你在項(xiàng)目中進(jìn)行變異測試。它在GitHub上有超過2000顆星標(biāo)。你可以點(diǎn)擊這里查看這個(gè)庫。
https://github.com/stryker-mutator/stryker-js
使用示例
Stryker是一個(gè)變異測試框架,可以幫助你提高單元測試的質(zhì)量。變異測試的工作原理是通過對(duì)代碼進(jìn)行小的修改(稱為“變異”),然后運(yùn)行你的單元測試以查看哪些修改沒有被測試捕獲,這可以幫助揭示代碼覆蓋率的盲點(diǎn)。
首先,你需要在你的項(xiàng)目中安裝Stryker和它需要的插件。在Node.js環(huán)境中,你可以使用npm(Node包管理器)來安裝:
npm install --save-dev @stryker-mutator/core @stryker-mutator/mocha-runner @stryker-mutator/javascript-mutator
在上面的示例中,我們安裝了Stryker的核心庫,用于運(yùn)行Mocha測試的運(yùn)行器以及JavaScript變異器。
然后,你需要?jiǎng)?chuàng)建一個(gè)Stryker配置文件。這個(gè)文件名通常為stryker.conf.js,并且應(yīng)該位于項(xiàng)目的根目錄下。在這個(gè)文件中,你可以定義Stryker應(yīng)該如何運(yùn)行你的測試和創(chuàng)建變異。
// 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作為測試運(yùn)行器。我們還告訴Stryker需要變異哪些文件。
現(xiàn)在,你可以運(yùn)行Stryker來執(zhí)行變異測試了:
npx stryker run
Stryker會(huì)生成一份報(bào)告,顯示每個(gè)變異是否被測試覆蓋。如果你的單元測試沒有捕獲到某個(gè)變異,那么你可能需要增加或改進(jìn)你的測試。
以上就是對(duì)Stryker庫的基本介紹和示例。你可以訪問其GitHub頁面獲取更多的信息和詳細(xì)的文檔。
你在編寫JavaScript測試,并在尋找一個(gè)模擬庫來替你模擬真實(shí)的東西嗎?這是一個(gè)有自己獨(dú)特見解的,設(shè)計(jì)精心的測試替身庫。該庫旨在適用于Node.js和瀏覽器解釋器。它也是測試框架無關(guān)的,所以你可以將它放入使用Jasmine、Mocha、Tape、Jest或我們自己的teenytest的代碼庫中。它在GitHub上有超過1000顆星標(biāo)。你可以點(diǎn)擊這里查看這個(gè)庫。
https://github.com/testdouble/testdouble.js
使用示例
TestDouble.js 是一個(gè)用于在JavaScript中創(chuàng)建測試替身(test doubles)的庫。它的設(shè)計(jì)原則是讓你能夠在單元測試中輕松地模擬或偽造(fake)依賴,從而讓你能夠更好地隔離和控制你的測試環(huán)境。
首先,你需要在你的項(xiàng)目中安裝TestDouble。在Node.js環(huán)境中,你可以使用npm(Node包管理器)來安裝:
npm install --save-dev testdouble
接下來,你可以在你的單元測試中使用TestDouble。例如,你可以使用td.function()來創(chuàng)建一個(gè)模擬函數(shù):
const td=require('testdouble');
// 創(chuàng)建一個(gè)模擬函數(shù)
const mockFunction=td.function();
// 使模擬函數(shù)在調(diào)用時(shí)返回特定的值
td.when(mockFunction('hello')).thenReturn('world');
// 現(xiàn)在,當(dāng)你調(diào)用 mockFunction('hello') 時(shí),它將返回 'world'
console.log(mockFunction('hello')); // 輸出: 'world'
你也可以使用TestDouble來模擬對(duì)象,例如使用td.object()來創(chuàng)建一個(gè)模擬對(duì)象:
const td=require('testdouble');
// 創(chuàng)建一個(gè)模擬對(duì)象
const mockObject=td.object(['method1', 'method2']);
// 使模擬對(duì)象的方法在調(diào)用時(shí)返回特定的值
td.when(mockObject.method1()).thenReturn('hello');
// 現(xiàn)在,當(dāng)你調(diào)用 mockObject.method1() 時(shí),它將返回 'hello'
console.log(mockObject.method1()); // 輸出: 'hello'
TestDouble.js 還提供了許多其他用于創(chuàng)建和管理測試替身的功能,例如驗(yàn)證函數(shù)是否被調(diào)用,替換模塊等。以上就是對(duì)TestDouble庫的基本介紹和示例,你可以訪問其GitHub頁面獲取更多的信息和詳細(xì)的文檔。
HTTP測試是最常見且支持最好的用例。這個(gè)庫讓你能夠在JavaScript中快速、可靠、在任何地方攔截、轉(zhuǎn)換或測試HTTP請(qǐng)求和響應(yīng)。你可以在集成測試中使用這個(gè)庫,作為你的測試套件的一部分來攔截真實(shí)的請(qǐng)求,或者你可以使用它來構(gòu)建自定義的HTTP代理,捕獲、檢查和/或以任何你喜歡的方式重寫HTTP。你可以點(diǎn)擊這里查看這個(gè)庫。
https://github.com/httptoolkit/mockttp
使用示例
Mockttp是一個(gè)強(qiáng)大的庫,它允許你在JavaScript中攔截、檢查和修改HTTP請(qǐng)求和響應(yīng)。這對(duì)于集成測試和調(diào)試HTTP通信非常有用。
首先,你需要在你的項(xiàng)目中安裝Mockttp。在Node.js環(huán)境中,你可以使用npm(Node包管理器)來安裝:
npm install --save-dev mockttp
接下來,我們將介紹一些基本的使用方式:
// 引入需要的庫
const superagent=require("superagent");
const mockServer=require("mockttp").getLocal();
// 在測試開始前啟動(dòng)Mock服務(wù)器,并在測試結(jié)束后關(guān)閉服務(wù)器
beforeEach(()=> mockServer.start(8080));
afterEach(()=> mockServer.stop());
// 模擬請(qǐng)求,并對(duì)結(jié)果進(jìn)行斷言
it("lets you mock requests, and assert on the results", async ()=> {
// 模擬你的端點(diǎn)
await mockServer.forGet("/mocked-path").thenReply(200, "A mocked response");
// 發(fā)送一個(gè)請(qǐng)求
const response=await superagent.get("http://localhost:8080/mocked-path");
// 對(duì)結(jié)果進(jìn)行斷言
expect(response.text).to.equal("A mocked response");
});
以上代碼創(chuàng)建了一個(gè)Mock服務(wù)器,并設(shè)置了一個(gè)模擬的GET請(qǐng)求。然后,我們發(fā)送一個(gè)實(shí)際的GET請(qǐng)求,并斷言返回的響應(yīng)文本是否等于我們?cè)O(shè)置的模擬響應(yīng)。
Mockttp還提供了更多高級(jí)特性,例如:
以下是一些更高級(jí)的示例:
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");
});
// 驗(yàn)證mock服務(wù)器接收的請(qǐng)求詳情
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`);
});
// 代理請(qǐng)求到任何其他主機(jī)
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的測試工具配合使用,可以模擬來自任何庫、工具或設(shè)備的請(qǐng)求。
在這篇文章中,我們了解了七個(gè)JavaScript測試庫:Jest、Sinon、Detox、Cucumber、Stryker、TestDouble和Mockttp。每一個(gè)庫都有其獨(dú)特的功能和特點(diǎn),可以幫助我們更高效地編寫和管理測試用例,確保代碼的質(zhì)量和穩(wěn)定性。
不論你是初學(xué)者還是資深開發(fā)者,這些庫都將是你開發(fā)過程中強(qiáng)大的工具。我希望通過本文的介紹,你能更深入地了解這些庫,找到最適合你的工具。
在結(jié)束本文之前,我想說,測試是軟件開發(fā)中不可或缺的一部分,選擇和掌握合適的測試工具,可以讓我們的工作變得更加輕松。最后,希望本文能對(duì)你的開發(fā)工作帶來幫助,如果你有任何問題或者建議,歡迎在評(píng)論區(qū)留言。感謝閱讀,我們下次再見。
由于文章內(nèi)容篇幅有限,今天的內(nèi)容就分享到這里,文章結(jié)尾,我想提醒您,文章的創(chuàng)作不易,如果您喜歡我的分享,請(qǐng)別忘了點(diǎn)贊和轉(zhuǎn)發(fā),讓更多有需要的人看到。同時(shí),如果您想獲取更多前端技術(shù)的知識(shí),歡迎關(guān)注我,您的支持將是我分享最大的動(dòng)力。我會(huì)持續(xù)輸出更多內(nèi)容,敬請(qǐng)期待。
文為霍格沃茲測試學(xué)院優(yōu)秀學(xué)員關(guān)于 Jacoco 的小結(jié)和踩坑記錄。測試開發(fā)進(jìn)階學(xué)習(xí),文末加群。
測試覆蓋率是老生常談的話題。因?yàn)槲覝y試?yán)碚摶A(chǔ)不是很好,這里就不提需求、覆蓋率等內(nèi)容,直奔主題,本文主要指 Java 后端的測試覆蓋率。
由于歷史原因,公司基本不做 UT,所以對(duì)測試來說,咱最關(guān)心的還是手工執(zhí)行、接口執(zhí)行 (人工 Postman 之類的)、接口自動(dòng)化、WebUI 自動(dòng)化對(duì)一個(gè)應(yīng)用系統(tǒng)的覆蓋度。
本來 Jacoco 已經(jīng)流行了很多年了,各種文檔和帖子已經(jīng)描述的很完美了,但是多數(shù)文章都是針對(duì)某一特定形式做了總結(jié)和使用。相信很多負(fù)責(zé)整個(gè)公司項(xiàng)目的覆蓋率任務(wù)的人們來說,還是要一種一種去研究、去應(yīng)對(duì),入坑、出坑不厭其煩。
也得益于今年上半年一直負(fù)責(zé)整個(gè)公司不同類型的項(xiàng)目的覆蓋率統(tǒng)計(jì)技術(shù)的適配,對(duì)不同形式的項(xiàng)目均有一定的了解,在此記錄一下,也不讓千瘡百孔的自己浪費(fèi)掉這半年的精力,如果說可以幫到別人一星半點(diǎn),那這篇文章就算是造福了。由于本人能力有限、表達(dá)能力有限,如有錯(cuò)誤,還請(qǐng)大家多指正。
因?yàn)橹傲私膺^一部分 Jacoco 的機(jī)制,也知道它提供了很多強(qiáng)大的功能,以滿足不同形式的項(xiàng)目。但歸根結(jié)底,Jacoco 提供了 API,可以讓大家屏蔽不同類型的項(xiàng)目帶來的困擾。
Jacoco 官方的 Api 示例地址:
https://www.Jacoco.org/Jacoco/trunk/doc/api.html
個(gè)人認(rèn)為,以 Api 的方式來進(jìn)行操作,可以有以下好處:
可以屏蔽不同方式的構(gòu)建部署。如果你想把這個(gè)功能做成平臺(tái),那 API 想必是很好的一種方式。
也就是說,我只需要把 Jacoco 插樁到測試服務(wù)器上,暴露 TCP 的 IP 和端口,剩余的提取代碼執(zhí)行數(shù)據(jù)、生成覆蓋率報(bào)告,就可以用統(tǒng)一的方式進(jìn)行就好了。
眾所周知,Jacoco 官方提供了 Maven 插件方式、Ant 的 XML 方式,均有對(duì)應(yīng)的 dump 和 report 來進(jìn)行覆蓋率數(shù)據(jù)的 dump 和報(bào)告生成,大家如果有興趣可以研究一下,這里不贅述。
由于我所在的公司是個(gè)老牌公司,項(xiàng)目雜亂無章,技術(shù)五花八門。至今仍然有跑在 JDK6 上的。所以我個(gè)人認(rèn)為,影響 Jacoco 使用過程的,可能存在于以下幾點(diǎn)。
我司現(xiàn)有 JDK6、7、8,但實(shí)際上 jdk6 是個(gè)分水嶺,其他的都基本可以用 JDK8 來適配。
我司現(xiàn)有 Maven 構(gòu)建、ANT 構(gòu)建,想必有的公司還有用 Gradle 的。
Ant、Maven 插件啟動(dòng)、Java -jar 啟動(dòng)、Tomcat 啟動(dòng) war 包 (打包方式就隨便了)
稍后內(nèi)容也都基于這幾種不同實(shí)現(xiàn)方式做描述。如果接觸項(xiàng)目多的,基本就知道,很多時(shí)候測試還是不介入測試環(huán)境的發(fā)布,這一方面源于開發(fā)的不信任,他們認(rèn)為發(fā)布還是要抓在開發(fā)自己手里;另一方面也源于測試人員能力的跟不上,至少在我司很多測試人員確實(shí)不太懂如何發(fā)布(雖然現(xiàn)在慢慢有所緩解,越來越都的測試人員都從開發(fā)手中接了過來)。
線上部署、測試部署、開發(fā)部署,這幾個(gè)不同場景,可能用的方式都不同,至少在我接觸的項(xiàng)目大都是這樣。開發(fā)喜歡用插件的方式啟動(dòng)部署,因?yàn)榭炻铮?IDE 也支持,右鍵運(yùn)行一下基本在 IDE 就啟動(dòng)了,想想看如果你是開發(fā),在你本地 IDE 里調(diào)試的時(shí)候,需要打個(gè) war 包然后丟到 Tomcat 里,再啟動(dòng) Tomcat,你也不太樂意。
廢話不多說,步入正題。Jacoco 介入部署過程的本質(zhì),就是插樁,至于怎么插樁,跟接入階段有關(guān)系。可以是編譯時(shí)插樁、也可以是運(yùn)行時(shí)插樁,這就是所謂 Offline 模式和 On-the-fly 模式,我們也不過多于糾結(jié),我們選擇了 on-the-fly 模式。
所以歸結(jié)到本質(zhì),Jacoco 的 on-the-fly 模式的插樁過程,其實(shí)就是在測試環(huán)境部署的時(shí)候,讓 Jacoco 的相關(guān)工具,介入部署過程,也就是介入 class 文件的加載,在加載 class 的時(shí)候,動(dòng)態(tài)改變字節(jié)碼結(jié)構(gòu),插入 Jacoco 的探針。
本質(zhì):Jacoco 以 TCPserver 方式進(jìn)行插樁的本質(zhì),就是如果應(yīng)用啟動(dòng)過程中,進(jìn)行了 Jacoco 插樁,且成功了。它會(huì)在你當(dāng)前這個(gè)啟動(dòng)服務(wù)器中,在一個(gè)端口{$port}上,開啟一個(gè) TCP 服務(wù),這個(gè) TCP 服務(wù),會(huì)一直接收 Jacoco 的執(zhí)行覆蓋率信息并傳到這個(gè) TCP 服務(wù)上進(jìn)行保存。
既然是個(gè) TCP 服務(wù),那 Jacoco 也提供了一種以 API 的方式連接到這個(gè) TCP 服務(wù)上,進(jìn)行覆蓋率數(shù)據(jù)的 dump 操作。(細(xì)節(jié)可能描述的不是很精確,但差不多就是這么個(gè)過程。這個(gè) TCP 服務(wù),在你沒有關(guān)閉應(yīng)用的時(shí)候,是一直開著的,可以隨時(shí)接受連接)
再本質(zhì)一點(diǎn),就是介入下面這個(gè)命令的啟動(dòng)過程:
java -jar
那問題就好辦了,一種一種來對(duì)應(yīng)起來。
提到介入啟動(dòng)過程,那就免不了提一下一個(gè) jar 包。
Jacocoagent.jar下載地址:
https://www.eclemma.org/Jacoco/
下載后解壓文件夾里,目錄如下:
這個(gè) Jacocoagent.jar, 就是啟動(dòng)應(yīng)用時(shí)主要用來插樁的 jar 包。
請(qǐng)注意不要寫錯(cuò)名稱,里面有個(gè)很像的 Jacocoant.jar,這個(gè) jar 包是用 ant xml 方式操作 Jacoco 時(shí)使用的,不要混淆。
以測試環(huán)境部署在 Linux 服務(wù)器上為例,如果想在 Windows 上測試也可以,把對(duì)應(yīng)的值改成 Windows 上識(shí)別的即可。
假設(shè) Jacocoagent.jar 的存放路徑為:/home/admin/Jacoco/Jacocoagent.jar
以下都以 $JacocoJarPath 來替代這個(gè)路徑,請(qǐng)注意這個(gè)路徑不是死的,你可以修改。
依然是基于上述的幾種不同方式,那我們針對(duì)不同形式·做插樁,也就是改變這幾種不同形式的底層啟動(dòng)原理,也就是改動(dòng)不同方式的 java 的啟動(dòng)參數(shù),這對(duì)每一種啟動(dòng)方式都不太一樣。但是改動(dòng) Java 啟動(dòng)參數(shù)本質(zhì)也是一樣的,就是在 java -jar 啟動(dòng)的時(shí)候,加入 -javaagent 參數(shù)。
-javaagent:$JacocoJarPath=includes=*,output=TCPserver,port=2014,address=192.168.110.1"
換成實(shí)際的信息為如下,請(qǐng)注意替換真實(shí)路徑,這一句是需要介入應(yīng)用啟動(dòng)過程的主要代碼,針對(duì)每種不同的部署方式,需要加到不同的地方。
-javaagent:/home/admin/Jacoco/Jacocoagent.jar=includes=*,output=TCPserver,port=2014,address=192.168.110.1
JDK5 之后新增的參數(shù),主要用來在運(yùn)行 jar 包的時(shí)候,以一種方式介入字節(jié)碼加載過程,如有興趣自行百度。注意后面有個(gè)冒號(hào):
需要用來介入 class 文件加載過程的 jar 包,想深入了解的,百度 “插樁” 哈。這是一個(gè) jar 包的絕對(duì)路徑。
這個(gè)代表了,啟動(dòng)時(shí)需要進(jìn)行字節(jié)碼插樁的包過濾,* 代表所有的 class 文件加載都需要進(jìn)行插樁。
假如你們公司內(nèi)部代碼都有相同的包前綴 :com.mycompany<你可以寫成:
includes=com.mycompany.*
這個(gè)地方不用改動(dòng),代表以 TCPserver 方式啟動(dòng)應(yīng)用并進(jìn)行插樁。
這是 Jacoco 開啟的 TCPserver 的端口,請(qǐng)注意這個(gè)端口不能被占用。
這是對(duì)外開發(fā)的 TCPserver 的訪問地址。可以配置 127.0.0.1, 也可以配置為實(shí)際訪問 IP。
配置為 127.0.0.1 的時(shí)候,dump 數(shù)據(jù)只能在這臺(tái)服務(wù)器上進(jìn)行 dump,就不能通過遠(yuǎn)程方式 dump 數(shù)據(jù)。配置為實(shí)際的 IP 地址的時(shí)候,就可以在任意一臺(tái)機(jī)器上 (前提是 IP 要通,不通都白瞎),通過 Ant XML 或者 API 方式 dump 數(shù)據(jù)。舉個(gè)栗子:
我如上配置了 192.168.110.1:2014 作為 Jacoco 的 TCPserver 啟動(dòng)服務(wù),那我可以在任意一臺(tái)機(jī)器上進(jìn)行數(shù)據(jù)的 dump,比如在我本機(jī) Windows 上用 API 或者 XML 方式調(diào)用 dump。
如果我配置了 127.0.0.1:2014 作為啟動(dòng)服務(wù)器,那么我只能在這臺(tái)測試機(jī)上進(jìn)行 dump,其他的機(jī)器都無法連接到這個(gè) TCPserver 進(jìn)行 dump。
這句內(nèi)容,如下,格式是固定的,只有括號(hào)內(nèi)的東西方可改變,其它盡量不要?jiǎng)樱B空格都不要多:
-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
注意其他地方基本不用改動(dòng)。
tomcat 的 war 包方式啟動(dòng),假設(shè) tomcat 路徑為: $CATALINA_HOME=/usr/local/apache-tomcat-8.5.20,我們常用的命令存在于: $CATALINA_HOME\bin下,有 startup.sh 和 shutdown.sh(windows 請(qǐng)自覺改為 bat, 后續(xù)不再聲明),其實(shí)這兩個(gè)只是封裝之后的腳本,底層調(diào)用的都是 $CATALINA_HOME\bin\catalina.sh(或者 bat),如圖源碼:
因此,只需要改動(dòng) catalina.sh 中的啟動(dòng)參數(shù)即可。
前面提到過,主要改動(dòng)主要是改動(dòng) java -jar,tomcat 是通過一個(gè) JAVA_OPTS 參數(shù)來控制額外的 java 啟動(dòng)參數(shù)的,我們只需要在合適的地方把上面的啟動(dòng)命令追加到 JAVA_OPTS 即可打開 catalina.sh,找到合適的地方修改 JAVA_OPTS 參數(shù):
理論上,任何地方修改 JAVA_OPTS 參數(shù)均可,但我們實(shí)驗(yàn)過后,在以下位置加入,是一定可以啟動(dòng)成功的,當(dāng)然您也可以嘗試其他位置。
JAVA_OPTS="$JAVA_OPTS -Dorg.apache.catalina.security.SecurityListener.UMASK=`umask`"
源腳本中有這個(gè)注釋掉的地方,我們?cè)谙路叫薷?JAVA_OPTS,在其下方,加一句:
JAVA_OPTS="$JAVA_OPTS -javaagent:$JacocoJarPath=includes=*,output=TCPserver,port=2014,address=192.168.110.1"
改完之后如下所示:
改完之后,就可以進(jìn)行 startup.sh 的啟動(dòng)了,應(yīng)用啟動(dòng)成功之后,可以在服務(wù)器上進(jìn)行調(diào)試,查看 TCPserver 是否真的起來了。
判別方式如下 (該圖中是現(xiàn)有的已經(jīng)開啟的服務(wù),所以 IP 和端口跟前面的命令不一樣,這點(diǎn)請(qǐng)注意,這里只是為了展示;后續(xù)幾種方式判別方式相同,不再贅述了哈), 這個(gè)端口在應(yīng)用啟動(dòng)時(shí)被占用,在應(yīng)用關(guān)閉時(shí)被釋放,這個(gè)請(qǐng)注意檢查:
如此,這個(gè)端口已經(jīng)在監(jiān)聽了,證明這個(gè)測試環(huán)境已經(jīng)把 Jacoco 注入進(jìn)去,那你對(duì)該測試環(huán)境的任何操作,代碼執(zhí)行信息都會(huì)被記錄到這個(gè) ip:port 開啟的 TCP 服務(wù)中。
在我司,有的開發(fā)會(huì)喜歡用插件方式啟動(dòng),在代碼 pom 文件層級(jí)中,運(yùn)行如下命令:
mvn clean install
mvn tomcat7:run -Dport=xxx
或者還有
mvn clean install
mvn spring-boot:run -Dport=xxx
這兩套命令,本質(zhì)上沒什么差別,只是運(yùn)行插件不一樣,具體用什么命令,如果不清楚,最好是跟開發(fā)請(qǐng)教一下。
他們的意思是,在當(dāng)前代碼的 pom 文件層級(jí)運(yùn)行,意思是通過 maven 的 tomcat 插件啟動(dòng)這個(gè)服務(wù),這個(gè)服務(wù)啟動(dòng)在端口 xxx 上,注意這個(gè)端口是應(yīng)用的訪問端口,和 Jacoco 的那個(gè)端口不是一回事。
對(duì)這種方式注入 Jacoco,也是可以的。這種可以不用修改任何的配置文件,只需要在你啟動(dòng)的時(shí)候,臨時(shí)修改變量就行了。這種方式改變 java 的啟動(dòng)參數(shù)方式是這樣:
export MAVEN_OPTS="-javaagent:$JacocoJarPath=includes=*,output=TCPserver,port=2014,address=192.168.110.1"
這句命令加在哪里呢?就是 run 之前。為什么呢,因?yàn)檫@樣一改,你的所有的 mvn 命令都會(huì)生效,但其實(shí)我們只想介入啟動(dòng)過程。因此,前面提到的兩套啟動(dòng)命令,就可以改成如下方式:
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=""
當(dāng)然,你的 run 命令,也可能是其他變種,比如:nohup mvn …. & 這種后臺(tái)啟動(dòng)的方式,也是可以的。最后修改為 "" 是因?yàn)閾?dān)心對(duì)后續(xù)的 mvn 命令產(chǎn)生影響,其實(shí)如果你切換了 terminal 窗口,這個(gè)臨時(shí)變量就會(huì)失效,不會(huì)對(duì)環(huán)境造成污染。
如果應(yīng)用啟動(dòng)成功了,就可以按照前面的方式,netstat 叛別一下 TCP 服務(wù)是否真的啟動(dòng)。
如果你設(shè)置了這個(gè)變量的位置不對(duì),那你用 mvn 命令的時(shí)候,可能會(huì)出現(xiàn)如下的異常:
java.net.BindException: Address already in use: bind
這時(shí)候,就需要去檢查一些,你配置的 Jacoco 端口是不是在啟動(dòng)應(yīng)用服務(wù)時(shí)已經(jīng)被占用。或者你臨時(shí)設(shè)置了 MAVEN_OPTS 這個(gè)變量,啟動(dòng)之后又沒有改回來,然后接著運(yùn)行了 mvn 命令,這時(shí)候也會(huì)出現(xiàn)這種錯(cuò)誤。這里請(qǐng)務(wù)必關(guān)注。
提一句題外話,ANT 的方式是不是也可以通過臨時(shí)修改 ANT_OPTS 參數(shù)進(jìn)行啟動(dòng) (因?yàn)?ANT 和 MAVEN 本是一家子嗎,我猜底層可能差異不是很大),我不曾做嘗試,有興趣的可以嘗試下。
這種方式可能實(shí)現(xiàn)啟動(dòng)應(yīng)用的階段不同,但大都配置在 build.xml 里,這里請(qǐng)根據(jù)不同的項(xiàng)目做不同的適配。
它的原理是,在 Ant 的啟動(dòng) target 中,有個(gè) 的標(biāo)簽,給她增加一個(gè) jvmarg 參數(shù)的子標(biāo)簽,如下代碼:
<jvmarg value=”-javaagent:$JacocoJarPath=includes=*,output=TCPserver,port=2014,address=192.168.110.1” />
比如我們的啟動(dòng)命令是這樣:
ant -f build.xml clean build startJetty
以此啟動(dòng)之后,將會(huì)注入 Jacoco 的代理,最終可以按照上面的方式判斷端口是否啟動(dòng)。
這種最簡單直接:
java -javaagent: $JacocoJarPath=includes=*,output=TCPserver,port=2014,address=192.168.110.1 -jar xxxxxxxxxx.jar
注意,javaagent 參數(shù),一定要在 jar 包路徑之前,盡量在-jar 之前,不然可能不會(huì)生效。請(qǐng)注意 java -jar 命令的使用方式,在 jar 包前面?zhèn)鬟M(jìn)去的是給 jvm 啟動(dòng)參數(shù)的,在 jar 包之后跟的是給 main 方法的。
啟動(dòng)后,依然按照前面的方式判斷是否啟動(dòng)了監(jiān)聽端口。
啟動(dòng)之后,就進(jìn)行測試就可以了,跟平常不注入 Jacoco 代理是無異的。
想繼續(xù)觀看精彩內(nèi)容可看下文。
(文章來源于霍格沃茲測試學(xué)院)
于前端程序員來說,V8引擎無疑是最為熟悉的工具之一了。V8是Google開源的JavaScript和WebAssembly引擎,用C++編寫。它用于Chrome和Node.js等。V8可以獨(dú)立運(yùn)行,也可以嵌入到任何C++應(yīng)用程序中。
為了測試V8作為JavaScript引擎的性能,Google隨后也開發(fā)了一套V8基準(zhǔn)測試套件,在運(yùn)行時(shí),V8基準(zhǔn)套件會(huì)載入一些特定的JavaScript代碼,從而測試引擎的內(nèi)核、加密、解密、渲染等速度。而該套件也就成為了JavaScript引擎性能的標(biāo)準(zhǔn)。
在該套件的第七個(gè)版本中,一共包括了八項(xiàng)基準(zhǔn)測試,最終得分為這八項(xiàng)測試得分的幾何平均數(shù)。得分越高表明速度越快。這八項(xiàng)測試的具體內(nèi)容如下:
一、Richards基準(zhǔn)
操作系統(tǒng)內(nèi)核的模擬基準(zhǔn), 最早出現(xiàn)于Matin Richards開發(fā)的BCPL中(539 行)。
主要關(guān)注點(diǎn):屬性加載/存儲(chǔ)、函數(shù)/方法調(diào)用
次要關(guān)注點(diǎn):代碼優(yōu)化、消除冗余代碼
二、DeltaBlue基準(zhǔn)
單向約束求解,最早出現(xiàn)于 John Maloney 和 Mario Wolczko開發(fā)的Smalltalk中 (880 行)。
主要關(guān)注點(diǎn):多態(tài)
次要關(guān)注點(diǎn):OO 樣式編程
三、Crypto基準(zhǔn)
Tom Wu開發(fā)的以代碼為基礎(chǔ)的加密解密基準(zhǔn)(1698 行)。
主要關(guān)注點(diǎn):位運(yùn)算
四、RayTrace基準(zhǔn)
Adam Burmister開發(fā)的以代碼為基礎(chǔ)的光線追蹤基準(zhǔn) (904 行)。
主要關(guān)注點(diǎn):參數(shù)對(duì)象,應(yīng)用
次要關(guān)注點(diǎn):原型庫對(duì)象,創(chuàng)建模式
五、EarleyBoyer基準(zhǔn)
經(jīng)典Scheme 基準(zhǔn), 由Florian Loitsch的Scheme2Js編譯器翻譯為JavaScript (4684 行)。
主要關(guān)注點(diǎn):快速創(chuàng)建、銷毀對(duì)象
次要關(guān)注點(diǎn):閉包, 參數(shù)對(duì)象
六、RegExp基準(zhǔn)
正則表達(dá)式基準(zhǔn),從50多個(gè)最流行的網(wǎng)頁中提取正則表達(dá)式操作所產(chǎn)生的(1761 行)。
關(guān)注點(diǎn):正則表達(dá)式
七、Splay基準(zhǔn)
數(shù)據(jù)操作基準(zhǔn),處理伸展樹和執(zhí)行自動(dòng)內(nèi)存管理子系統(tǒng) (394 行)。
主要關(guān)注點(diǎn):快速創(chuàng)建、銷毀對(duì)象
八、NavierStokes基準(zhǔn)
根據(jù)奧利弗·亨特的代碼,在2D上解決navierstokes方程,重操縱雙精度數(shù)組。(387 行).
主要關(guān)注點(diǎn):讀取和寫入數(shù)字?jǐn)?shù)組。
次要關(guān)注點(diǎn):浮點(diǎn)數(shù)學(xué)運(yùn)算。
V8基準(zhǔn)測試在早期的JavaScript引擎開發(fā)過程中應(yīng)用廣泛,很多JS引擎都使用該基準(zhǔn)測試用于評(píng)測其性能。常用的JS引擎測試結(jié)果如下:
盡管V8基準(zhǔn)測試套件非常經(jīng)典,但是隨著技術(shù)的發(fā)展,Google又推出了新的基準(zhǔn)測試套件Octane 1.0和2.0,陸續(xù)增加了下列九項(xiàng)測試基準(zhǔn):
pdf.js:在JavaScript中實(shí)現(xiàn)了Mozilla的PDF閱讀器。它可以測量解碼和解釋的時(shí)間(33,056行)。
主要關(guān)注點(diǎn):數(shù)組和類型化數(shù)組操作。
次要關(guān)注點(diǎn):數(shù)學(xué)運(yùn)算和位運(yùn)算,以及對(duì)未來語言功能(例如 promise)的支持
SplayLatency:Splay 測試側(cè)重于虛擬機(jī)的垃圾回收子系統(tǒng)。SplayLatency 對(duì)現(xiàn)有 Splay 代碼進(jìn)行頻繁測量檢查點(diǎn)插樁。檢查點(diǎn)之間長時(shí)間暫停表示 GC 延遲時(shí)間較長。此測試衡量延遲暫停的頻率,將它們分類為分桶,并根據(jù)低分懲罰頻繁地長暫停。
主要關(guān)注點(diǎn):垃圾回收延遲
Mandreel:運(yùn)行3D Bullet物理引擎,該引擎通過Mandreel將C++移植到JavaScript (277377行)。
主要關(guān)注點(diǎn):模擬
MandreelLatency:與SplayLatency 測試類似,此測試通過頻繁的時(shí)間測量點(diǎn)對(duì) Mandreel 基準(zhǔn)進(jìn)行插樁。由于 Mandreel 對(duì)虛擬機(jī)編譯器施加壓力,因此該測試會(huì)提供編譯器引入的延遲指示。在測量點(diǎn)之間長時(shí)間暫停會(huì)降低最終得分。
主要關(guān)注點(diǎn):編譯器延遲時(shí)間
GB Emulator:全部采用JavaScript模擬便攜式控制臺(tái)的架構(gòu),以及運(yùn)行所需的3D模擬(11,097行)。
主要關(guān)注點(diǎn):模擬
Code loading:測量Javascript引擎在加載了一段大型的Javascript程序后開始解碼的速度有多快,一個(gè)常見的實(shí)例為Social Widget。該測試的源代碼來自開源代碼庫(Closure, jQuery)(1,530行)。
主要關(guān)注內(nèi)容:JavaScript 解析和編譯
Box2DWeb:基于流行的2D物理引擎Box2DWeb,最初由Erin Catto編寫,現(xiàn)被移植到JavaScript。(560行,9000+ 精簡版)
主要關(guān)注點(diǎn):浮點(diǎn)數(shù)學(xué)運(yùn)算。
次要關(guān)注點(diǎn):包含 Double 的屬性、訪問器屬性。
Zlib:從 Mozilla Emscripten 套件執(zhí)行的 zlib asm.js/Emscripten 測試(在工作負(fù)載 1 中運(yùn)行)。代碼包含在 eval() 中,它保證我們測量的運(yùn)行時(shí)間包括在所有瀏覽器上解析和編譯(2,585 行)。
主要關(guān)注點(diǎn):代碼編譯和執(zhí)行
Typescript:Microsoft&Type 39 TypeScript 編譯器是一款復(fù)雜的應(yīng)用。此測試用于衡量 TypeScript 編譯本身所需的時(shí)間,它代表虛擬機(jī)在處理復(fù)雜、可調(diào)整大小的 JavaScript 應(yīng)用(25918 行)方面的表現(xiàn)。
主要關(guān)注點(diǎn):運(yùn)行復(fù)雜、繁重的應(yīng)用
除此之外,常用的JavaScript基準(zhǔn)測試工具還有Mozilla發(fā)布的Kraken、蘋果的JetStream、以及Speedometer和Speed-Battle等。
喜歡本文的話,歡迎關(guān)注活在信息時(shí)代哦:)
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。