前端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示例,重點介紹了前端應用的程式碼掃描和測試覆蓋率確認的簡單方式。