整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          分享7個(gè)最佳的JavaScript測試庫,提高你的工

          分享7個(gè)最佳的JavaScript測試庫,提高你的工作效率

          載說明:原創(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)目的測試工具。

          1、Jasmine

          這是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ì)的文檔。

          2、Sinon

          這是一個(gè)獨(dú)立的庫,用于在JavaScript測試中創(chuàng)建測試替身(偵查、樁和模擬)。它通過提供工具來驗(yàn)證函數(shù)調(diào)用、控制行為等,幫助你編寫隔離的測試。它在GitHub上有超過9000顆星標(biāo)。你可以點(diǎn)擊這里查看這個(gè)庫。

          https://github.com/sinonjs/sinon

          3、Detox

          如果你想對(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ì)的文檔。

          4、Cucumber

          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ì)的文檔。

          5、Stryker

          變異測試會(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ì)的文檔。

          6、TestDouble

          你在編寫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ì)的文檔。

          7、Mockttp

          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í)特性,例如:

          1. 無需指定端口,允許并行測試
          2. 驗(yàn)證Mock服務(wù)器接收的請(qǐng)求詳情
          3. 代理請(qǐng)求到任何其他主機(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)求。

          結(jié)尾

          在這篇文章中,我們了解了七個(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)告生成,大家如果有興趣可以研究一下,這里不贅述。

          三、項(xiàng)目梳理

          由于我所在的公司是個(gè)老牌公司,項(xiàng)目雜亂無章,技術(shù)五花八門。至今仍然有跑在 JDK6 上的。所以我個(gè)人認(rèn)為,影響 Jacoco 使用過程的,可能存在于以下幾點(diǎn)。

          1. JDK 版本。

          我司現(xiàn)有 JDK6、7、8,但實(shí)際上 jdk6 是個(gè)分水嶺,其他的都基本可以用 JDK8 來適配。

          1. 構(gòu)建工具。

          我司現(xiàn)有 Maven 構(gòu)建、ANT 構(gòu)建,想必有的公司還有用 Gradle 的。

          1. 部署方式。

          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ì)

          廢話不多說,步入正題。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

          5.1 這句話的解釋

          1. -javaagent

          JDK5 之后新增的參數(shù),主要用來在運(yùn)行 jar 包的時(shí)候,以一種方式介入字節(jié)碼加載過程,如有興趣自行百度。注意后面有個(gè)冒號(hào):

          1. /home/admin/Jacoco/Jacocoagent.jar

          需要用來介入 class 文件加載過程的 jar 包,想深入了解的,百度 “插樁” 哈。這是一個(gè) jar 包的絕對(duì)路徑。

          1. includes=*

          這個(gè)代表了,啟動(dòng)時(shí)需要進(jìn)行字節(jié)碼插樁的包過濾,* 代表所有的 class 文件加載都需要進(jìn)行插樁。

          假如你們公司內(nèi)部代碼都有相同的包前綴 :com.mycompany<你可以寫成:

          includes=com.mycompany.*
          1. output=TCPserver

          這個(gè)地方不用改動(dòng),代表以 TCPserver 方式啟動(dòng)應(yīng)用并進(jìn)行插樁。

          1. port=2014

          這是 Jacoco 開啟的 TCPserver 的端口,請(qǐng)注意這個(gè)端口不能被占用。

          1. address=192.168.110.1

          這是對(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。

          1. 總結(jié):

          這句內(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)。

          5.2 war 包方式啟動(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ù)中。

          5.3 Maven 命令的插件啟動(dòng)方式

          在我司,有的開發(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 本是一家子嗎,我猜底層可能差異不是很大),我不曾做嘗試,有興趣的可以嘗試下。

          5.4 ANT 構(gòu)建,通過 XML 配置文件啟動(dòng)

          這種方式可能實(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)。

          5.5 java -jar 方式啟動(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)聽端口。

          5.6 啟動(dòng)之后

          啟動(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í)代哦:)


          主站蜘蛛池模板: 国产一区二区三区电影| 亚洲爽爽一区二区三区| 精品一区二区三区免费视频| 亚洲AV无码一区二区三区系列| 国产成人一区二区三区免费视频 | 国产精品久久久久一区二区三区| 亚洲片一区二区三区| 毛片一区二区三区无码| 欧洲无码一区二区三区在线观看| 亲子乱av一区二区三区| 波多野结衣的AV一区二区三区| 国产伦精品一区二区三区精品| 国产怡春院无码一区二区 | 亚洲AV网一区二区三区| 亚洲国产精品综合一区在线| 无码国产精品一区二区免费vr| 国产丝袜视频一区二区三区| 国产在线一区二区综合免费视频 | 高清一区二区三区日本久| 中文字幕永久一区二区三区在线观看 | 无码人妻精品一区二区蜜桃百度| 国产精品亚洲专一区二区三区| 在线不卡一区二区三区日韩| 无码国产精品一区二区高潮| 无码人妻aⅴ一区二区三区有奶水| 亚洲av无码一区二区三区天堂| 一本大道在线无码一区| 国产乱码精品一区二区三区香蕉| 亚洲熟妇AV一区二区三区宅男| 成人精品一区二区户外勾搭野战| 亚洲AV日韩AV一区二区三曲| 日韩在线视频一区| 一级特黄性色生活片一区二区| chinese国产一区二区| 人妻少妇一区二区三区| 日韩精品无码一区二区三区免费| 精品视频一区二区三三区四区| 伊人无码精品久久一区二区| 日韩一区二区免费视频| 琪琪see色原网一区二区| 国产熟女一区二区三区四区五区|