成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

AngularJs directive 的單元測(cè)試方法

JouyPub / 2881人閱讀

摘要:翻譯自在這篇文章中,我將詳述如何給我們上周開發(fā)的做單元測(cè)試的過程。單元測(cè)試是一種測(cè)試你的項(xiàng)目中每個(gè)最小單元代碼的藝術(shù),是使你的程序思路清晰的基礎(chǔ)。

第一次翻譯技術(shù)文章,肯定很多語(yǔ)句很生疏,有看官的話就見諒,沒有的話也沒人看的到這句話。。

翻譯自:Unit Testing an AngularJS Directive

在這篇文章中,我將詳述如何給我們上周開發(fā)的stepper directive做單元測(cè)試的過程。下周會(huì)講到如何使用Github和Bower進(jìn)行組件分離。

單元測(cè)試是一種測(cè)試你的項(xiàng)目中每個(gè)最小單元代碼的藝術(shù),是使你的程序思路清晰的基礎(chǔ)。一旦所有的測(cè)試通過,這些零散的單元組合在一起也會(huì)運(yùn)行的很好,因?yàn)檫@些單元的行為已經(jīng)被獨(dú)立的驗(yàn)證過了。

單元測(cè)試能夠避免你的代碼出現(xiàn)回歸性BUG,提高代碼的質(zhì)量和可維護(hù)性,使你的代碼在代碼庫(kù)中是可信賴的,從而提高團(tuán)隊(duì)合作的質(zhì)量,使重構(gòu)變得簡(jiǎn)單和快樂: )

單元測(cè)試的另一個(gè)用處是當(dāng)你發(fā)現(xiàn)了一個(gè)新的BUG,你可以為這個(gè)BUG寫一個(gè)單元測(cè)試,當(dāng)你修改了你的代碼,使這個(gè)測(cè)試可以PASS了的時(shí)候,就說(shuō)明這個(gè)BUG已經(jīng)被修復(fù)了。

AngularJS最好的小伙伴兒KarmaJS test runner(一個(gè)能夠在瀏覽器中運(yùn)行測(cè)試同時(shí)生成結(jié)果日志的Node.js server)還有 Jasmine(定義了你的測(cè)試和斷言的語(yǔ)法的庫(kù))。我們使用Grunt-karma將karma集成在我們經(jīng)典且繁重的grunt 工作流中,然后在瀏覽器中運(yùn)行測(cè)試。這里值得注意的是,karma可以將測(cè)試運(yùn)行在遠(yuǎn)程的云瀏覽器中,比如SauceLabs和BrowserStack。

AngularJS是將是經(jīng)過了嚴(yán)密地測(cè)試的,所以趕緊給自己點(diǎn)個(gè)贊,現(xiàn)在就開始寫測(cè)試吧!

術(shù)語(yǔ):

在我們進(jìn)行下一步之前有一些術(shù)語(yǔ)需要說(shuō)明:

spec: 你想要測(cè)試的代碼的說(shuō)明,包括一個(gè)或多個(gè)測(cè)試條件。spec應(yīng)該覆蓋所有預(yù)期行為。

test suite: 一組測(cè)試的集合,定義在Jasmine提供的describe語(yǔ)句塊中,語(yǔ)句塊是可以嵌套的。

test: 測(cè)試說(shuō)明,寫在Jasmin提供的it語(yǔ)句塊中,以一個(gè)或者多個(gè)期望值結(jié)束(譯者按:也就是說(shuō),一個(gè)it語(yǔ)句塊中,一定要有一個(gè)以上的期望值)。

actual: 在你的期望中要被測(cè)試的值。

expected value: 針對(duì)測(cè)試出的真實(shí)值做比較的期望值。(原文:this is the value you test the actual value against.)

matcher: 一個(gè)返回值為Boolean類型的函數(shù),用于比較真實(shí)值跟期望值。結(jié)果返回給jasmine,比如toEqual,toBeGreatherThan,toHaveBeenCalledWith... 你也可以定義你自己的matcher。

expectation: 使用expect函數(shù)測(cè)試一個(gè)值,得到它的返回值,expectation是與一個(gè)得到期望值的matcher函數(shù)鏈接的。(原文:Use the expect function to test a value, called the actual. It is chained with a matcher function, which takes the expected value.)

mock: 一種「stubbed」(不會(huì)翻譯)服務(wù),你可以制造一些假數(shù)據(jù)或方法來(lái)替代程序真正運(yùn)行時(shí)所產(chǎn)生的數(shù)據(jù)。

這有一個(gè)spec文件的例子:


// a test suite (group of tests) //一組測(cè)試 describe("sample component test", function() { // a single test //多帶帶的測(cè)試 it("ensure addition is correct", function() { // sample expectation // 簡(jiǎn)單的期望 expect(1+1).toEqual(2); // `--- the expected value (2) 期望值是2 // `--- the matcher method (equality) toEqual方法就是matcher函數(shù) // `-- the actual value (2) 真實(shí)值是2 }); // another test // 另一個(gè)測(cè)試 it("ensure substraction is correct", function() { expect(1-1).toEqual(0); }); });
測(cè)試環(huán)境搭建

將grunt-karma添加到你項(xiàng)目的依賴中

npm install grunt-karma --save -dev

創(chuàng)建一個(gè)karma-unit.js文件

這里是一個(gè)karma-unit文件的例子
這個(gè)文件定義了如下內(nèi)容:
* 將要被加載到瀏覽器進(jìn)行測(cè)試的JS文件。通常情況下,不僅項(xiàng)目用的庫(kù)和項(xiàng)目本身的文件需要包含在內(nèi),你所要測(cè)試的文件和mock文件也要在這里加載。
* 你想將測(cè)試運(yùn)行在哪款瀏覽器中。
* 怎樣接收到測(cè)試結(jié)果,是命令行里還是在瀏覽器中...?
* 可選插件。

以下是files這一項(xiàng)的例子:

files: [
  "http://code.angularjs.org/1.2.1/angular.js",       <-- angular sourc
  "http://code.angularjs.org/1.2.1/angular-mocks.js", <-- angular mocks & test utils
  "src/angular-stepper.js",                           <-- our component source code
  "src/angular-stepper.spec.js"                       <-- our component test suite
]

注:這里可以添加jquery在里面,如果你需要它幫助你編寫測(cè)試代碼(更強(qiáng)大的選擇器,CSS測(cè)試,尺寸計(jì)算…)

將karma grunt tasks添加到Gruntfile.js中

karma: {
    unit: {
        configFile: "karma-unit.js",
        // run karma in the background
        background: true,
        // which browsers to run the tests on
        browsers: ["Chrome", "Firefox"]
    }
}

然后創(chuàng)建 angular-stepper.spec.js文件,將上面寫的簡(jiǎn)單的測(cè)試代碼粘貼進(jìn)來(lái)。這時(shí)你就可以輕松運(yùn)行grunt karma任務(wù)去觀察你的測(cè)試在瀏覽器中運(yùn)行并且在命令行中生成測(cè)試報(bào)告。

....
Chrome 33.0.1712 (Mac OS X 10.9.0): Executed 2 of 2 SUCCESS (1.65 secs / 0.004 secs)
Firefox 25.0.0 (Mac OS X 10.9): Executed 2 of 2 SUCCESS (2.085 secs / 0.006 secs)
TOTAL: 4 SUCCESS

上面有四個(gè)點(diǎn),每個(gè)點(diǎn)都代表一個(gè)成功的測(cè)試,這時(shí)你可以看到,兩個(gè)測(cè)試分別運(yùn)行在我們配置的兩個(gè)瀏覽器中了。
哦也~

那么接下來(lái),讓我們寫一些真正的測(cè)試代碼吧: )

給directive編寫單元測(cè)試

為我們的組件所編寫的一組單元測(cè)試,又叫做spec的東西,不僅應(yīng)該覆蓋我們所要測(cè)試的組件的所有預(yù)期行為,還要將邊緣情況覆蓋到(比如不合法的輸入、服務(wù)器的異常狀況)。

下面展示的angular-stepper組件的測(cè)試集的精華部分,完整版點(diǎn)這里。我們對(duì)這樣一個(gè)組件的測(cè)試非常簡(jiǎn)單,不需要假數(shù)據(jù)。唯一比較有技巧性的是,我們將我們的directive包含在了一個(gè)form表單下,這樣能夠在使用ngModelController和更新表單驗(yàn)證正確性的情況下正確的運(yùn)行測(cè)試。(注:此處的內(nèi)容需要讀angular-stepper那個(gè)組件的文件才能懂為何要將directive包含在form表單中,如果不想深入了解,可以忽略這句。原文:The only tricky thing is that we wrap our directive inside a form to be able to test that it plays well with ngModelController and updates form validity correctly.)


// the describe keyword is used to define a test suite (group of tests) describe("rnStepper directive", function() { // we declare some global vars to be used in the tests var elm, // our directive jqLite element scope; // the scope where our directive is inserted // load the modules we want to test 在跑測(cè)試之前將你要測(cè)試的模塊引入進(jìn)來(lái) beforeEach(module("revolunet.stepper")); // before each test, creates a new fresh scope // the inject function interest is to make use of the angularJS // dependency injection to get some other services in our test inject方法的作用是利用angularJS的依賴注入將我們所需要的服務(wù)注入進(jìn)去 // here we need $rootScope to create a new scope 需要用$rootScope新建一個(gè)scope beforeEach(inject(function($rootScope, $compile) { scope = $rootScope.$new(); scope.testModel = 42; })); function compileDirective(tpl) { // function to compile a fresh directive with the given template, or a default one // compile the tpl with the $rootScope created above // wrap our directive inside a form to be able to test // that our form integration works well (via ngModelController) // our directive instance is then put in the global "elm" variable for further tests if (!tpl) tpl = "
"; tpl = "
" + tpl + "
"; //原文最后一個(gè)標(biāo)簽是感覺是筆誤。 // inject allows you to use AngularJS dependency injection // to retrieve and use other services inject(function($compile) { var form = $compile(tpl)(scope); elm = form.find("div"); }); // $digest is necessary to finalize the directive generation //$digest 方法對(duì)于生成指令是必要的。 scope.$digest(); } describe("initialisation", function() { // before each test in this block, generates a fresh directive beforeEach(function() { compileDirective(); }); // a single test example, check the produced DOM it("should produce 2 buttons and a div", function() { expect(elm.find("button").length).toEqual(2); expect(elm.find("div").length).toEqual(1); }); it("should check validity on init", function() { expect(scope.form.$valid).toBeTruthy(); }); }); it("should update form validity initialy", function() { // test with a min attribute that is out of bounds // first set the min value scope.testMin = 45; // then produce our directive using it compileDirective("
"); // this should impact the form validity expect(scope.form.$valid).toBeFalsy(); }); it("decrease button should be disabled when min reached", function() { // test the initial button status compileDirective("
"); expect(elm.find("button").attr("disabled")).not.toBeDefined(); // update the scope model value scope.testModel = 40; // force model change propagation scope.$digest(); // validate it has updated the button status expect(elm.find("button").attr("disabled")).toEqual("disabled"); }); // and many others... });

一些需要注意的點(diǎn):

在要被測(cè)試的scope中,一個(gè)directive需要被compiled(譯者注:也就是上面代碼中的$compile(tpl)(scope);這句話在做的事情)。
一個(gè)非隔離scope可以通過element.scope()方法訪問到。
一個(gè)隔離的scope可以通過element.isolateScope()方法訪問到。

為啥我在改變一個(gè)Model的值的時(shí)候需要調(diào)用scope.$digest()方法?

在一個(gè)真正的angular應(yīng)用中,$digest方法是angular通過各種事件(click,inputs,requests...)的反應(yīng)自動(dòng)調(diào)用的。自動(dòng)化測(cè)試不是以真實(shí)的用戶事件為基礎(chǔ)的,所以我們需要手動(dòng)的調(diào)用$digest方法($digest方法負(fù)責(zé)更新所有數(shù)據(jù)綁定)。

額外福利 #1: 實(shí)時(shí)測(cè)試

多虧了grunt,當(dāng)我們的文件改動(dòng)的時(shí)候,可以自動(dòng)的進(jìn)行測(cè)試。

如果你想在你的代碼有任何改動(dòng)的時(shí)候都進(jìn)行一次測(cè)試,只要將一段代碼加入到grunt的watch任務(wù)中就行。

js: {
    files: ["src/*.js"],
    tasks: ["karma:unit:run", "build"]
},

你也可以將grunt的默認(rèn)任務(wù)設(shè)置成這樣:

grunt.registerTask("default", ["karma:unit", "connect", "watch"]);

設(shè)置完后,運(yùn)行g(shù)runt,就可以實(shí)時(shí)的在內(nèi)置的server中跑測(cè)試了。

額外福利 #2:添加測(cè)試覆蓋率報(bào)告

作為開發(fā)者,我們希望以靠譜的數(shù)據(jù)作為依據(jù),我們也希望持續(xù)的改進(jìn)自己的代碼。"coverage"指的是你的測(cè)試代碼的覆蓋率,它可以提供給你一些指標(biāo)和詳細(xì)的信息,無(wú)痛的增加你的代碼的覆蓋率。

下面是一個(gè)簡(jiǎn)易的覆蓋率報(bào)告:

我們可以詳細(xì)的看到每個(gè)文件夾的每個(gè)文件的代碼是否被測(cè)試覆蓋。歸功于grunt+karma的集成,這個(gè)報(bào)告是實(shí)時(shí)更新的。我們可以在每一個(gè)文件中一行一行的檢查那一塊帶按摩沒有被測(cè)試。這樣能使測(cè)試變得更加的簡(jiǎn)單。

100% test coverage 不代表你的代碼就沒有BUG了,但它代表這代碼質(zhì)量的提高!

karma+grunt的集成特別的簡(jiǎn)單,karma有一套「插件」系統(tǒng),它允許我們通過配置karma-unit.js文件來(lái)外掛fantastic Istanbul 代碼覆蓋率檢測(cè)工具。只要配置一下文件,媽媽就再也不用擔(dān)心我的代碼覆蓋率了。

Add coverage to karma
# add the necessary node_modules
npm install karma-coverage --save-dev

現(xiàn)在將新的設(shè)置更新到kamar的配置文件中

// here we specify which of the files we want to appear in the coverage report
preprocessors: {
    "src/angular-stepper.js": ["coverage"]
},
// add the coverage plugin
plugins: [ "karma-jasmine", "karma-firefox-launcher", "karma-chrome-launcher", "karma-coverage"],
// add coverage to reporters
reporters: ["dots", "coverage"],
// tell karma how you want the coverage results
coverageReporter: {
  type : "html",
  // where to store the report
  dir : "coverage/"
}

更多覆蓋率的設(shè)置請(qǐng)看這里:https://github.com/karma-runner/karma-coverage

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/78078.html

相關(guān)文章

  • angular 1.x多項(xiàng)目共享子項(xiàng)目實(shí)踐之路

    摘要:可發(fā)布這一部分會(huì)在下一章管理對(duì)子項(xiàng)目引用中詳細(xì)說(shuō)明??偨Y(jié)本文總結(jié)了多項(xiàng)目共享子項(xiàng)目工程化方面的一些實(shí)踐,并不涉及到復(fù)雜的代碼,主要涉及到的概念,使用進(jìn)行包管理,使用作為自動(dòng)化工具等工程化的知識(shí)。 背景 公司的產(chǎn)品線涵蓋多個(gè)產(chǎn)品,這些產(chǎn)品中會(huì)有一些相同的功能,如登錄,認(rèn)證等,為了保持這些功能在各個(gè)產(chǎn)品中的一致性,我們?cè)诟鱾€(gè)產(chǎn)品中維護(hù)一份相同的代碼。這帶來(lái)了很大的不便:當(dāng)出現(xiàn)新的需求時(shí),不...

    mist14 評(píng)論0 收藏0
  • angular2.0 筆記 - 01

    angular2.0 學(xué)習(xí)筆記 ### 1.angular-cli 常用命令記錄 詳細(xì)教程 angular cli官網(wǎng) 有,這里不詳細(xì)說(shuō)明,感興趣的可以自行到官網(wǎng)看,一下僅記錄本人到學(xué)習(xí)過程常用到的命令 1.創(chuàng)建項(xiàng)目 ng new ng new project-name exp: ng new my-app 2.啟動(dòng)項(xiàng)目 ng serve 參數(shù)名 類型 默認(rèn)值 作用 exp ...

    AnthonyHan 評(píng)論0 收藏0
  • Angularjs學(xué)習(xí)筆記指令

    摘要:自定義指令中有很多內(nèi)置指令,一般都是以開頭的比如等等。本文介紹的自定義指令的用法。該參數(shù)的意思是替換指令的內(nèi)容,更改上面的例子。將屬性綁定到父控制器的域中學(xué)習(xí)概念多指令中的參數(shù)中增加了的值和的點(diǎn)擊函數(shù)。 自定義指令 angularjs中有很多內(nèi)置指令,一般都是以ng開頭的;比如:ng-app,ng-click,ng-repeat等等。本文介紹angularjs的自定義指令的用法。 指令...

    LeexMuller 評(píng)論0 收藏0
  • Angularjs學(xué)習(xí)筆記指令

    摘要:自定義指令中有很多內(nèi)置指令,一般都是以開頭的比如等等。本文介紹的自定義指令的用法。該參數(shù)的意思是替換指令的內(nèi)容,更改上面的例子。將屬性綁定到父控制器的域中學(xué)習(xí)概念多指令中的參數(shù)中增加了的值和的點(diǎn)擊函數(shù)。 自定義指令 angularjs中有很多內(nèi)置指令,一般都是以ng開頭的;比如:ng-app,ng-click,ng-repeat等等。本文介紹angularjs的自定義指令的用法。 指令...

    Cristic 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<