1. 程式人生 > >前端Javascript程式碼質量掃描解決方案:Karma + Jasmine + lcov + Sonarqube

前端Javascript程式碼質量掃描解決方案:Karma + Jasmine + lcov + Sonarqube

程式碼質量掃描的流行工具sonarqube,java系的應用開發框架可以使用Maven/Gradle作為構建工具,JUnit作為單體覆蓋率測試工具,使用Jacoco視覺化提供支撐。而Javascript則可以使用Karma+Jasmine+lcov+Sonarqube進行類似的質量掃描。

環境準備&說明

node版本

事前安裝node,本文示例中使用如下版本

liumiaocn:~ liumiao$ npm -v
5.5.1
liumiaocn:~ liumiao$ node -v
v8.9.1
liumiaocn:~ liumiao$ 

karma安裝與介紹

安裝karma

使用npm install -g karma進行karma的安裝

liumiaocn:~ liumiao$ npm set registry="https://registry.npm.taobao.org/"
liumiaocn:~ liumiao$ npm install -g karma
... 
+ [email protected]
added 328 packages in 13.582s
liumiaocn:~ liumiao$

版本確認

liumiaocn:~ liumiao$ karma --version
Karma version: 3.1.4
liumiaocn:~ liumiao$

karma簡介

Karma是google為AngularJS開發的測試工具,當然後面更名的Angular同樣在使用。除了Angular的應用之外,普通的Javascript程式也可以使用Karma。Karma在使用的時候通過Karma start啟動伺服器端應用(預設使用9876埠),而客戶端使用瀏覽器與之建立連線。伺服器端應用會監視原始碼或測試程式碼的更新,而無需手動重新整理瀏覽器,通過類似的改進,使用karma可以快速的在不同的瀏覽器中進行自動化單元測試。

安裝瀏覽器啟動器外掛

karma在啟動伺服器的時候,可以同時通過瀏覽器啟動器外掛啟動瀏覽器,而這些瀏覽器和伺服器應用建立連線。

瀏覽器 瀏覽器啟動器名稱
FIREFOX karma-firefox-launcher
CHROME karma-chrome-launcher
  • 安裝啟動器外掛(chrome):npm install -g karma-chrome-launcher
liumiaocn:~ liumiao$ npm install -g karma-chrome-launcher
+ [email protected]
added 5 packages in 1.546s
liumiaocn:~ liumiao$ 
  • 瀏覽器可執行檔案目錄的設定:

通過設定 瀏覽器名稱_BIN來進行環境變數的設定

瀏覽器 環境變數
FIREFOX瀏覽器 FIREFOX_BIN
CHROME瀏覽器 CHROME_BIN

安裝測試框架介面卡

Karma支援Jasmine/Mocha等多種測試框架,比如安裝Jasmine介面卡:

  • npm install -g karma-jasmine
liumiaocn:~ liumiao$ npm install -g karma-jasmine
...省略
+ [email protected]
added 2 packages in 1.2s
liumiaocn:~ liumiao$ 

基於BDD(行為驅動開發)的Jasmine是一個JS的測試框架框架。使用Jasmine可以快速地進行測試程式碼的開發。

安裝測試報告外掛

通過不同報告外掛,比如JUnit格式和HTML格式:

格式 外掛名稱
JUnit格式 karma-junit-reporter
HTML格式 karma-html-reporter
  • npm install -g karma-html-reporter
  • npm install -g karma-junit-reporter
liumiaocn:~ liumiao$ npm install -g karma-html-reporter
...省略
+ [email protected]
added 3 packages in 1.455s
liumiaocn:~ liumiao$ npm install -g karma-junit-reporter
...省略
+ [email protected]
added 3 packages in 1.349s
liumiaocn:~ liumiao$ 

安裝karma-coverage

liumiaocn:~ liumiao$ npm install -g karma-coverage
+ [email protected]
added 95 packages in 9.97s
liumiaocn:~ liumiao$

建立karma例程

用karma init生成karma.conf.js

使用karma init建立簡單示例用的測試的設定檔案karma.conf.js

liumiaocn:~ liumiao$ mkdir karma
liumiaocn:~ liumiao$ cd karma
liumiaocn:karma liumiao$ karma init

Which testing framework do you want to use ?
Press tab to list possible options. Enter to move to the next question.
> jasmine

Do you want to use Require.js ?
This will add Require.js plugin.
Press tab to list possible options. Enter to move to the next question.
> no

Do you want to capture any browsers automatically ?
Press tab to list possible options. Enter empty string to move to the next question.
> Chrome
> 

What is the location of your source and test files ?
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".
Enter empty string to move to the next question.
> 

Should any of the files included by the previous patterns be excluded ?
You can use glob patterns, eg. "**/*.swp".
Enter empty string to move to the next question.
> 

Do you want Karma to watch all the files and run the tests on change ?
Press tab to list possible options.
> yes


Config file generated at "/Users/liumiao/karma/karma.conf.js".
liumiaocn:karma liumiao$

karma init只有一個作用,就是生成karma的設定檔案,此檔案預設名稱為karma.conf.js,可以通過 karma init 指定檔名稱的方式來修改設定檔名稱。
具體相關的設定說明:

步驟 提示 說明 示例設定值
設定項1 Which testing framework do you want to use ? 選擇測試框架,比如mocha或者jasmine等 jasmine
設定項2 Do you want to use Require.js ? 是否需要引入Require.js no:不需要
設定項3 Do you want to capture any browsers automatically ? 選擇測試所使用的瀏覽器,如果選擇多個將會在多個瀏覽器中進行測試 Chrome
設定項4 What is the location of your source and test files ? 支援正則表示式的原始碼和測試程式碼的目錄設定 未設定(後將手動設定)
設定項5 Should any of the files included by the previous patterns be excluded ? Step 4中需要排除在外的檔案(支援正則) 未設定
設定項6 Do you want Karma to watch all the files and run the tests on change ? 是否觀察檔案變化 yes:觀察檔案變化

構建前端單體測試示例

結合上述生成的檔案,整體建立如下結構的組成:

liumiaocn:karma liumiao$ ls
karma.conf.js            sonar-project.properties src                      test
liumiaocn:karma liumiao$ tree
.
├── karma.conf.js
├── sonar-project.properties
├── src
│   └── cal.js
└── test
    └── cal.spec.js

2 directories, 4 files
liumiaocn:karma liumiao$ 
  • 詳細說明
檔名 說明
karma.conf.js karma的工程設定檔案
src/cal.js javascript原始碼,一個加減乘除的函式
test/cal.spec.js javascript測試程式碼,用於測試cal.js
sonar-project.properties sonar-scanner工程設定檔案

程式碼說明:src/cal.js

用於實現加減乘除的函式:

  • 函式名稱:cal
  • 引數1: op1(運算元1)
  • 引數2: op2(運算元2)
  • 引數3: optype:型別(加減乘除)
  • 返回值:返回運算的結果
liumiaocn:karma liumiao$ cat src/cal.js 
function cal(op1,op2,optype){
    if ( optype === 'add' ) {
      return op1 + op2;
    } else if ( optype === 'sub' ) {
      return op1 - op2;
    } else if ( optype === 'mul' ) {
      return op1 * op2;
    } else if ( optype === 'div' ) {
      return op1 / op2;
    }
}
liumiaocn:karma liumiao$ 

程式碼說明:test/cal.spec.js

在說明測試程式碼之前,首先理解一下使用Jasmine進行開發的一些基礎概念和Jasmine中的實現:

  • 測試用例集:Test Suite:以函式describe進行封裝和實現
  • 測試用例:Test Case:以函式it進行分裝和實現,在Jasmine中稱為Specs
  • 斷言:Asset:以函式expect進行分裝和實現,進行計算值和期望值之間是否一致的確認
liumiaocn:karma liumiao$ cat test/cal.spec.js 
describe('cal test suit',function(){
    it('test cal add',function(){
        var ret = cal(40,2,'add');
        expect(ret).toEqual(42);
    });

    it('test cal mul',function(){
        var ret = cal(21,2,'mul');
        expect(ret).toEqual(42);
    });
});
liumiaocn:karma liumiao$ 

上面的測試程式碼可以清楚地看到測試了加法和乘法兩個分支。這是一個非常見的的測試實際使用起來會複雜地多,比如常見的describe的巢狀等,由於篇幅的關係,本文只是介紹前端javascript的一個通用的程式碼質量掃描和測試覆蓋率結合的整體的方式,重點在於各個流程的貫通。

設定karma.conf.js

前文使用karma init生成了karma.conf.js檔案,結合測試的javascript原始碼和測試程式碼,將karma.conf.js檔案進行設定。
使用karma init中的設定項4(What is the location of your source and test files ?)中設定的內容,可設定原始碼和測試程式碼的目錄設定,這裡做如下設定:

    files: [
      'src/*.js',
      'test/*.spec.js'
    ],

同時設定一下preprocessors,在此處定義的方法,會在檔案被瀏覽器執行前執行。

    preprocessors: {
      'src/*.js' : ['coverage'],
      'test/*.js' : ['coverage']
    },

在Javascript的單體測試中,lcov就像java中的jacoco一樣,用於提供測試用例執行之後的相關資料記錄,根據這個檔案karam可以生成相關的測試覆蓋率報告,同樣sonarqube也是同樣會利用lcov的檔案。在karma.conf.js中的coverageReporter進行如下設定,則可在根目錄的reports/coverage下生成相關的測試結果檔案。

    coverageReporter: {
      type : 'lcov',
      dir : 'reports',
      subdir : 'coverage'
    },

將coverage加入reporters中

reporters: ['progress','coverage'],

這樣就完成了Karma的設定,如此,Karma + Jasmine + lcov的合體就能夠正常動作了。

karma動作確認

使用karma start可以執行上述測試用例並能生成結果檔案。 執行方式可以為

karma start

或者

karma start karma.conf.js

  • 設定引數
引數 說明
–port 指定伺服器執行的埠
–auto-watch 自動監控原始碼檔案並基於變更執行
–detached 從伺服器斷開
–no-auto-watch 不監控原始碼檔案
–log-level 日誌級別:disable
–colors 報表和日誌展示的顏色設定
–no-colors 報表和日誌展示時不使用顏色
–reporters reporter列表(包括: dots, progress, junit, growl, coverage).
–browsers 瀏覽器列表 (示例: --browsers Chrome,ChromeCanary,Firefox).
–capture-timeout 超時時間 [單位:ms].
–single-run 執行後推出的方式
–no-single-run 不實用single-run方式
–fail-on-empty-test-suite 空測試用例集失敗
–no-fail-on-empty-test-suite 空測試用例集繼續執行
–fail-on-failing-test-suite 測試用例集失敗退出
–no-fail-on-failing-test-suite 測試用例集失敗不退出

比如single-run 都是很有用的Option,可以使得karma的伺服器端應用執行之後直接退出,可根據具體場景進行使用。

liumiaocn:karma liumiao$ ls
karma.conf.js            sonar-project.properties src                      test
liumiaocn:karma liumiao$ karma start --single-run
27 12 2018 21:40:08.621:INFO [karma-server]: Karma v3.1.4 server started at http://0.0.0.0:9876/
27 12 2018 21:40:08.623:INFO [launcher]: Launching browsers Chrome with concurrency unlimited
27 12 2018 21:40:08.628:INFO [launcher]: Starting browser Chrome
27 12 2018 21:40:09.568:INFO [Chrome 71.0.3578 (Mac OS X 10.14.0)]: Connected on socket 98YmcgcevgWq06DWAAAA with id 98376372
Chrome 71.0.3578 (Mac OS X 10.14.0): Executed 2 of 2 SUCCESS (0.003 secs / 0.003 secs)
TOTAL: 2 SUCCESS
liumiaocn:karma liumiao$

因為是single-run方式,可以看到一閃而過的chrome瀏覽器,並在控制檯上看見以上資訊,提示執行了兩個測試用例而且全部成功,然後來確認一下結果。

liumiaocn:karma liumiao$ ls
karma.conf.js            reports                  sonar-project.properties src                      test
liumiaocn:karma liumiao$ tree reports
reports
└── coverage
    ├── lcov-report
    │   ├── base.css
    │   ├── index.html
    │   ├── prettify.css
    │   ├── prettify.js
    │   ├── sort-arrow-sprite.png
    │   ├── sorter.js
    │   ├── src
    │   │   ├── cal.js.html
    │   │   └── index.html
    │   └── test
    │       ├── cal.spec.js.html
    │       └── index.html
    └── lcov.info

4 directories, 11 files
liumiaocn:karma liumiao$

可以看到生成了上述提到的結果目錄以及lcov.info檔案, 接下來通過reports/coverage/lcov-report/index.html來確認結果

liumiaocn:lcov-report liumiao$ pwd
/Users/liumiao/easypack/karma/reports/coverage/lcov-report
liumiaocn:lcov-report liumiao$ ls
base.css              prettify.css          sort-arrow-sprite.png src
index.html            prettify.js           sorter.js             test
liumiaocn:lcov-report liumiao$ 
  • 整體的檔案的測試覆蓋狀況的整體資訊
    在這裡插入圖片描述

  • 檢視原始碼檔案src/cal.js的測試覆蓋整體狀況
    在這裡插入圖片描述

  • 檢視原始碼檔案src/cal.js的程式碼測試覆蓋詳細
    在這裡插入圖片描述

+ Sonarqube

如果結合sonarqube進行結果的整體確認,可以使用sonar-scanner,而相關的設定資訊如下所示:

liumiaocn:karma liumiao$ cat sonar-project.properties 
sonar.projectKey=karma-demo
sonar.projectName=Karma Demo Project
sonar.projectVersion=1.0
sonar.sources=src
sonar.host.url=http://127.0.0.1:32003
sonar.login=admin
sonar.password=admin
sonar.javascript.lcov.reportPath=reports/coverage/lcov.info
liumiaocn:karma liumiao$

其他的sonar的設定,因為在sonarquebe的使用方式中多次做過詳細介紹,此處不再贅述。而與前端測試的覆蓋率展示相關的僅有sonar.javascript.lcov.reportPath而已,sonar.javascript.lcov.reportPath是用與設定lcov的相關檔案路徑,而在上述檔案中也看到過此檔案的生成。

  • 執行方式
    通過呼叫sonar-scanner即可(sonar-scanner只需要下載下來,然後將可執行檔案放到PATH中即可,所以此處也不再贅述)。
liumiaocn:karma liumiao$ ls
karma.conf.js            reports                  sonar-project.properties src                      test
liumiaocn:karma liumiao$ sonar-scanner
INFO: Scanner configuration file: /Users/liumiao/Desktop/sonar/sonar-scanner-3.2.0.1227-macosx/conf/sonar-scanner.properties
INFO: Project root configuration file: /Users/liumiao/easypack/karma/sonar-project.properties
INFO: SonarQube Scanner 3.2.0.1227
INFO: Java 1.8.0_121 Oracle Corporation (64-bit)
INFO: Mac OS X 10.14 x86_64
INFO: User cache: /Users/liumiao/.sonar/cache
INFO: SonarQube server 5.6.5
INFO: Default locale: "en_US", source code encoding: "UTF-8" (analysis is platform dependent)
INFO: Load global repositories
INFO: Load global repositories (done) | time=127ms
INFO: User cache: /Users/liumiao/.sonar/cache
INFO: Load plugins index
INFO: Load plugins index (done) | time=3ms
INFO: Process project properties
INFO: Load project repositories
INFO: Load project repositories (done) | time=94ms
INFO: Load quality profiles
INFO: Load quality profiles (done) | time=56ms
INFO: Load active rules
INFO: Load active rules (done) | time=325ms
INFO: Publish mode
INFO: -------------  Scan Karma Demo Project
INFO: Load server rules
INFO: Load server rules (done) | time=64ms
INFO: Base dir: /Users/liumiao/easypack/karma
INFO: Working dir: /Users/liumiao/easypack/karma/.scannerwork
INFO: Source paths: src
INFO: Source encoding: UTF-8, default locale: en_US
INFO: Index files
INFO: 1 files indexed
INFO: Quality profile for js: Sonar way
INFO: JaCoCoSensor: JaCoCo report not found : /Users/liumiao/easypack/karma/target/jacoco.exec
INFO: JaCoCoItSensor: JaCoCo IT report not found: /Users/liumiao/easypack/karma/target/jacoco-it.exec
INFO: Sensor Lines Sensor
INFO: Sensor Lines Sensor (done) | time=10ms
INFO: Sensor JavaScriptSquidSensor
INFO: 1 source files to be analyzed
INFO: Sensor JavaScriptSquidSensor (done) | time=127ms
INFO: 1/1 source files have been analyzed
INFO: Sensor SCM Sensor
INFO: SCM provider for this project is: git
INFO: 1 files to be analyzed
INFO: 1/1 files analyzed
INFO: Sensor SCM Sensor (done) | time=218ms
INFO: Sensor org.sonar.plugins.javascript.lcov.UTCoverageSensor
INFO: Analysing /Users/liumiao/easypack/karma/reports/coverage/lcov.info
WARN: Could not resolve 1 file paths in lcov.info, first unresolved path: /Users/liumiao/easypack/karma/test/cal.spec.js
INFO: Sensor org.sonar.plugins.javascript.lcov.UTCoverageSensor (done) | time=13ms
INFO: Sensor org.sonar.plugins.javascript.lcov.ITCoverageSensor
INFO: Sensor org.sonar.plugins.javascript.lcov.ITCoverageSensor (done) | time=0ms
INFO: Sensor Zero Coverage Sensor
INFO: Sensor Zero Coverage Sensor (done) | time=6ms
INFO: Sensor Code Colorizer Sensor
INFO: Sensor Code Colorizer Sensor (done) | time=1ms
INFO: Sensor CPD Block Indexer
INFO: DefaultCpdBlockIndexer is used for js
INFO: Sensor CPD Block Indexer (done) | time=18ms
INFO: Calculating CPD for 1 files
INFO: CPD calculation finished
INFO: Analysis report generated in 55ms, dir size=12 KB
INFO: Analysis reports compressed in 15ms, zip size=5 KB
INFO: Analysis report uploaded in 31ms
INFO: ANALYSIS SUCCESSFUL, you can browse http://127.0.0.1:32003/dashboard/index/karma-demo
INFO: Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report
INFO: More about the report processing at http://127.0.0.1:32003/api/ce/task?id=AWcYZ67mTV5bsL-6UV8A
INFO: ------------------------------------------------------------------------
INFO: EXECUTION SUCCESS
INFO: ------------------------------------------------------------------------
INFO: Total time: 3.544s
INFO: Final Memory: 21M/279M
INFO: ------------------------------------------------------------------------
liumiaocn:karma liumiao$ 
  • 結果確認:整體掃描結果與測試覆蓋率
    在這裡插入圖片描述

  • 結果確認:程式碼粒度測試覆蓋詳細
    在這裡插入圖片描述

需要注意的是程式碼行數的計算和覆蓋率的細節,兩個工具略有不同,雖然影響不大,但是這是需要意識到的一個小細節。

原始碼

示例用的整體程式碼和生成的檔案上傳至github的easypack中,目錄如下:

總結

這樣結合karma和sonarqube,結合一個簡單的javascript示例,重點介紹了前端應用的程式碼掃描和測試覆蓋率確認的簡單方式。