javascript單元測試框架mochajs詳解

發表于:2016-08-03來源:鄒成卓的個人網站作者:鄒成卓點擊數: 標簽:單元測試框架
對于一些比較重要的項目,每次更新代碼之后總是要自己測好久,擔心一旦上線出了問題影響的服務太多,此時就希望能有一個比較規范的測試流程。在github上看到牛逼的javascript開源

關于單元測試的想法

對于一些比較重要的項目,每次更新代碼之后總是要自己測好久,擔心一旦上線出了問題影響的服務太多,此時就希望能有一個比較規范的測試流程。在github上看到牛逼的javascript開源項目,也都是有測試代碼的,看來業界大牛們都比較注重單元測試這塊。就我自己的理解而言:?我要說話

  • 涉及到大量業務邏輯的代碼,可能我沒有精力去給每個函數都寫上單元測試的代碼,功能細節的測試應該交由測試的同事去完成,但是對會直接影響項目正常運行的重要的數據接口,還是可以看情況寫上幾個單元測試用例的,每一次修改之后跑一跑用例測試一下。
  • 重要的框架底層模塊,任何地方出一個小問題,都可能影響到很多服務。對于這種模塊,最好是每個函數、每種接口都寫上單元測試代碼,不然一出問題就是一個大坑啊。
  • 開放出去的公共模塊,可以針對主要的函數和接口寫上單元測試代碼,這樣可以確保模塊代碼比較健壯,看起來也專業一些:)。

基于以上幾個想法,我決定學習一款Javascript單元測試框架,并試試去使用它寫一些單元測試的代碼??戳撕芏嗉夹g站點和博客的文章,參考了一部分開源項目的測試代碼,大致觀望下風向,決定學習一下mocha.js這款單元測試框架。別人的文章都是別人自己學習、咀嚼理解出來的內容,想學的透徹一點,還是自己學習并翻譯一遍原版官方的文檔比較好。?我要說話

mocha單元測試框架簡介

mocha是一款功能豐富的javascript單元測試框架,它既可以運行在nodejs環境中,也可以運行在瀏覽器環境中。javascript是一門單線程語言,最顯著的特點就是有很多異步執行。同步代碼的測試比較簡單,直接判斷函數的返回值是否符合預期就行了,而異步的函數,就需要測試框架支持回調、promise或其他的方式來判斷測試結果的正確性了。mocha可以良好的支持javascript異步的單元測試。mocha會串行地執行我們編寫的測試用例,可以在將未捕獲異常指向對應用例的同時,保證輸出靈活準確的測試結果報告。?我要說話

安裝mocha

npm install mocha -g?我要說話

一個簡單的例子

全局安裝mocha后,在項目根目錄創建test目錄編寫test01.js ,?我要說話

1
2
3
4
5
6
7
8
9
var assert = require('chai').assert;
describe('Array', function() {
  describe('#indexOf()', function() {
    it('should return -1 when the value is not present', function() {
      assert.equal(-1, [1,2,3].indexOf(5));
      assert.equal(-1, [1,2,3].indexOf(0));
    });
  });
});

?我要說話

npm install chai --save 安裝一下官方文檔中使用的chai斷言模塊(一個用來判斷結果是否正確的模塊)打開控制臺,在test目錄同級運行mocha,得到如下結果: Array?我要說話

  #indexOf()
    √ should return -1 when the value is not present
1 passing (11ms)

可以看到:describe函數的第一個參數會被輸出在控制臺中,作為一個用例集的描述,而且這個描述是可以根據自己的需求來嵌套輸出的,下面稱之為:用例集定義函數。it函數第一個參數用來輸出一個用例的描述,前邊打個對勾代表測試通過,第二個參數是一個函數,用來編寫用例內容,用斷言模塊來判斷結果的正確性,下面稱之為用例函數。?我要說話

mocha支持的斷言模塊

mocha支持任何可以拋出一個錯誤的斷言模塊。例如:should.js、better-assert、expect.js、unexpected、chai等。這些斷言庫各有各的特點,大家可以了解一下它們的特點,根據使用場景來選擇斷言庫。?我要說話

同步代碼測試

在測試同步代碼的時候,用例函數執行完畢后,mocha就直接開始執行下一個用例函數了。 下面是一個同步測試代碼的例子:?我要說話

1
2
3
4
5
6
7
8
describe('Array', function() {
  describe('#indexOf()', function() {
    it('should return -1 when the value is not present', function() {
      [1,2,3].indexOf(5).should.equal(-1);
      [1,2,3].indexOf(0).should.equal(-1);
    });
  });
});

?我要說話

異步代碼測試

官方文檔自稱,用mocha來測試異步的代碼不要再簡單!真的很自信啊~~只需要在用例函數里邊加一個done回調,異步代碼執行完畢后調用一下done,就可以通知mocha,我執行完啦,去執行下一個用例函數吧!就像下面這樣:?我要說話

1
2
3
4
5
6
7
8
9
10
11
describe('User', function() {
  describe('#save()', function() {
    it('should save without error', function(done) {
      var user = new User('Luna');
      user.save(function(err) {
        if (err) throw err;
        done();
      });
    });
  });
});

?我要說話

對于上面的情況,判斷用例執行成功與否是在異步代碼的回調里邊完成的,這種情況適用于正確性判斷比較復雜的情況。如果異步代碼中邏輯錯誤時,會在回調中拋出一個錯誤,那么測試代碼還可以再簡單一點:?我要說話

1
2
3
4
5
6
7
8
9
10
11
describe('User', function() {
  describe('#save()', function() {
    it('should save without error', function(done) {
      var user = new User('Luna');
      user.save(function(err) {
        if (err) throw err;
        done();
      });
    });
  });
});

?我要說話

promise代碼測試

如果異步模塊并不是使用callback,而是使用promise來返回結果的時候,可以讓用例函數返回一個promise對象來進行正確性判斷,像下面這樣:?我要說話

1
2
3
4
5
6
7
8
9
10
11
12
beforeEach(function() {
  return db.clear()
    .then(function() {
      return db.save([tobi, loki, jane]);
    });
});

describe('#find()', function() {
  it('respond with matching records', function() {
    return db.find({ type: 'User' }).should.eventually.have.length(3);
  });
});

?我要說話

后邊還會再提到這種情況?我要說話

不建議使用箭頭函數

不建議在mocha測試框架中使用箭頭函數。箭頭函數語法中對this的綁定讓會用例函數沒辦法訪問Mocha框架上下文中定義的一些函數,例如this.timeout(1000)在箭頭函數中就無法得到正確的結果。我對這里的理解是:mocha會把用例函數注冊到自身的某個屬性中,通過屬性調用的使用,正常函數可以訪問到mocha的其他屬性,但是箭頭函數不行,就像下面的代碼一樣:?我要說話

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function getTest(){
    this.a=555;
    var test={
        a:1,
        b:function(){
            console.log(this.a)
        },
        c:()=>{
        console.log(this.a);
    }
}
    return test;
}
var test=getTest();
test.b();
test.c();

?我要說話

輸出結果是 1 555?我要說話

鉤子函數

mocha提供4種鉤子函數:before()、after()、beforeEach()、afterEach(),這些鉤子函數可以用來在用例集/用例函數開始執行之前/結束執行之后,進行一些環境準備或者環境清理的工作。?我要說話

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
describe('hooks', function() {

  before(function() {
    // runs before all tests in this block
  });

  after(function() {
    // runs after all tests in this block
  });

  beforeEach(function() {
    // runs before each test in this block
  });

  afterEach(function() {
    // runs after each test in this block
  });

  // test cases
});

?我要說話

鉤子函數的描述參數

定義鉤子函數的時候,可以傳第一個可選參數作為鉤子函數的描述,可以幫助定位用例中的錯誤信息。若沒有穿第一個參數,使用一個非匿名函數作為鉤子函數,那么函數名稱就將被作為鉤子函數的描述。?我要說話

1
2
3
4
5
6
7
8
9
10
11
beforeEach(function() {
  // beforeEach hook
});

beforeEach(function namedFun() {
  // beforeEach:namedFun
});

beforeEach('some description', function() {
  // beforeEach:some description
});

?我要說話

異步的鉤子函數

鉤子函數不僅能是同步函數,也可能是異步的函數,就像前邊的異步測試用例函數一樣。如果我們在開始之前,需要做一些異步的操作,例如在數據庫里邊準備一些模擬數據,就需要用到這種場景了:?我要說話

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
describe('Connection', function() {
  var db = new Connection,
    tobi = new User('tobi'),
    loki = new User('loki'),
    jane = new User('jane');

  beforeEach(function(done) {
    db.clear(function(err) {
      if (err) return done(err);
      db.save([tobi, loki, jane], done);
    });
  });

  describe('#find()', function() {
    it('respond with matching records', function(done) {
      db.find({type: 'User'}, function(err, res) {
        if (err) return done(err);
        res.should.have.length(3);
        done();
      });
    });
  });
});

?我要說話

全局鉤子

前邊講的鉤子函數都是定義在用例集函數里邊的,如果在用例集函數之外定義鉤子函數,那么這個鉤子函數將會對所有的mocha單元測試用例生效。若編寫了多個用例集js文件,無論在哪一個用例集文件中,用例集函數之外定義鉤子函數,都會對所有用例函數生效。前邊講到,用例集函數是可以嵌套的,而mocha會生成一個包在最外面的describe函數,把所有的用例集包含起來,那么在其中定義的鉤子函數也就對所有的用例函數生效了~?我要說話

1
2
3
beforeEach(function() {
  console.log('before every test in every file');
});

?我要說話

延遲啟動測試

如果想在mocha命令運行之后,先做一些別的工作,再啟動測試,可以使用mocha --delay命令,此命令會在全局環境中生成一個run函數,延遲工作完成后調用run函數即可啟動測試。?我要說話

1
2
3
4
5
6
7
8
9
setTimeout(function() {
  // do some setup

  describe('my suite', function() {
    // ...
  });

  run();
}, 5000);

?我要說話

測試用例TODO

可以編寫一個等待被實現的測試用例,在報告里邊也會有提示?我要說話

1
2
3
4
5
6
describe('Array', function() {
  describe('#indexOf()', function() {
    // pending test below
    it('should return -1 when the value is not present');
  });
});

?我要說話

2 passing (11ms)
 1 pending

僅執行一個用例集/用例

在用例集函數或者用例函數后邊添加.only()可以讓mocha只執行此用例集或用例?我要說話

1
2
3
4
5
describe('Array', function() {
  describe.only('#indexOf()', function() {
    // ...
  });
});

?我要說話

Array用例集下面的嵌套集合,只有#indexOf用例集會被執行?我要說話

1
2
3
4
5
6
7
8
9
10
11
describe('Array', function() {
  describe('#indexOf()', function() {
    it.only('should return -1 unless present', function() {
      // ...
    });

    it('should return the index when present', function() {
      // ...
    });
  });
});

?我要說話

這種寫法,#indexOf用例集下面的用例,只有第一個加了only的會被執行。注意:在同一用例集下有多個only標記,mocha會報錯。?我要說話

跳過哪些用例集/用例

和加上.only相反,在用例集函數或者用例函數后邊加.skip(),可以跳過此用例集或用例的執行。跳過的用例會被標記為pending的用例,在報告中也會作為pending用例體現出來。下面是一些例子:?我要說話

1
2
3
4
5
describe('Array', function() {
  describe.skip('#indexOf()', function() {
    // ...
  });
});

?我要說話

跳過用例集?我要說話

1
2
3
4
5
6
7
8
9
10
11
describe('Array', function() {
  describe('#indexOf()', function() {
    it.skip('should return -1 unless present', function() {
      // ...
    });

    it('should return the index when present', function() {
      // ...
    });
  });
});

?我要說話

跳過用例。對于一些作廢的用例,按我們以往的做法可能就是把它注釋掉,mocha推薦的做法是給它們加上skip標記。?我要說話

除了使用添加skip標記之外,mocha還允許在用例執行的過程中跳過此用例,例如用例執行需要某些上下文環境,但是執行的時候發現這些環境并沒有準備好,此時就可以調用skip函數跳過此用例:?我要說話

1
2
3
4
5
6
7
it('should only test in the correct environment', function() {
  if (/* check test environment */) {
    // make assertions
  } else {
    this.skip();
  }
});

?我要說話

上面被跳過的用例同樣會在測試報告中以pending的形式體現出來。為了避免測試邏輯混亂,在調用skip函數之后,就不要再再用例函數或after鉤子中執行更多的邏輯了。?我要說話

這里要說明一點,在一個用例函數中,不要存在一個邏輯分支啥也不做,直接讓整個用例函數結束,這樣是不科學的。用例函數中應該至少使用斷言做一個判斷,或者調用skip函數跳過用例。?我要說話

1
2
3
4
5
6
7
it('should only test in the correct environment', function() {
  if (/* check test environment */) {
    // make assertions
  } else {
    // do nothing
  }
});

?我要說話

這樣的用例如果走到了do nothing邏輯,在報告中會被標記為pass。比較推薦的做法是,在before鉤子函數中檢查測試需要的上下文環境,不具備則跳過。?我要說話

1
2
3
4
5
6
7
before(function() {
  if (/* check test environment */) {
    // setup code
  } else {
    this.skip();
  }
});

?我要說話

重新執行用例

你可以指定讓一個失敗的用例重新執行一定次數。這個特性是為做end-to-end測試(功能性測試/Selenium測試)而設計的,這些測試數據不好模擬。 mocha是不推薦用這個特性來做單元測試的。?我要說話

這個特性會重新運行用例函數的beforeEach和afterEach鉤子函數,但是不會重新運行before和after鉤子函數。?我要說話

下面是一個重新執行的例子:?我要說話

1
2
3
4
5
6
7
8
9
10
11
12
13
14
describe('retries', function() {
  // Retry all tests in this suite up to 4 times
  this.retries(4);
  
  beforeEach(function () {
    browser.get('http://www.yahoo.com');
  });
  
  it('should succeed on the 3rd try', function () {
    // Specify this test to only retry up to 2 times
    this.retries(2);
    expect($('.foo').isDisplayed()).to.eventually.be.true;
  });
});

?我要說話

動態生成用例

mocha可以使用Function.prototype.call和函數表達式來定義用例集和用例,它們可以用來直接動態生成一些測試用例,而不需要使用其他額外的語法。和你可能在其他框架中見到的一樣,這個特性可以實現通過定義一些參數來實現測試用例的功能。?我要說話

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var assert = require('chai').assert;

function add() {
  return Array.prototype.slice.call(arguments).reduce(function(prev, curr) {
    return prev + curr;
  }, 0);
}

describe('add()', function() {
  var tests = [
    {args: [1, 2],       expected: 3},
    {args: [1, 2, 3],    expected: 6},
    {args: [1, 2, 3, 4], expected: 10}
  ];

  tests.forEach(function(test) {
    it('correctly adds ' + test.args.length + ' args', function() {
      var res = add.apply(null, test.args);
      assert.equal(res, test.expected);
    });
  });
});

?我要說話

上面的測試會生成下面這樣報告: add() ? correctly adds 2 args ? correctly adds 3 args ? correctly adds 4 args?我要說話

測試時間

許多測試報告會展示測試時間,同樣也會標記出那些用例耗時比較長:?我要說話

可能對于某些測試用例,耗時就是會比較長,那么耗費多長時間才應該本認為執行耗時過長呢? 可以通過slow()函數來標記一下:?我要說話

1
2
3
4
5
6
7
describe('something slow', function() {
  this.slow(10000);

  it('should take long enough for me to go make a sandwich', function() {
    // ...
  });
});

?我要說話

測試超時

用例集執行超時

在用例集下定義的timeout超時會對此用例集下定義的所有嵌套的用例集和用例生效,如果嵌套的用例集或者用例重寫了timeout時間,則會覆蓋上層的設置。通過this.timeout(0),可以關掉用例或用例集的超時判斷。?我要說話

1
2
3
4
5
6
7
8
9
10
11
describe('a suite of tests', function() {
  this.timeout(500);

  it('should take less than 500ms', function(done){
    setTimeout(done, 300);
  });

  it('should take less than 500ms as well', function(done){
    setTimeout(done, 250);
  });
})

?我要說話

用例執行超時

1
2
3
4
it('should take less than 500ms', function(done){
  this.timeout(500);
  setTimeout(done, 300);
});

鉤子函數超時

1
2
3
4
5
6
describe('a suite of tests', function() {
  beforeEach(function(done) {
    this.timeout(3000); // A very long environment setup.
    setTimeout(done, 2500);
  });
});

鉤子函數同樣可以通過this.timeout(0)來關閉超時判斷。?我要說話

diff差異比較功能

若斷言庫拋出了AssertionErrors,且錯誤對象中有err.expected屬性和err.actual屬性,mocha會嘗試在報告中展示期望的值和得到的值的差異:?我要說話

mocha使用命令和參數

mocha init 初始化瀏覽器中測試

mocha init命令用來生成一個瀏覽器中單元測試的架子。新建一個目錄test在同級目錄運行命令 mocha init test ,可以看到test目錄下生成了一些樣式表文件和js腳本,以及一個用來運行用例、展示報告的index.html接著使用文章開頭的chai斷言庫,此時需要用script標簽引入了,于是在index.html中加上?我要說話

1
<script src="http://chaijs.com/chai.js"></script>

?我要說話

index.html內容如下:?我要說話

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html>
  <head>
    <title>Mocha</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="mocha.css" />
  </head>
  <body>
    <div id="mocha"></div>
	<script src="http://chaijs.com/chai.js"></script>
    <script src="mocha.js"></script>
    <script>mocha.setup('bdd');</script>
    <script src="tests.js"></script>
    <script>
      mocha.run();
    </script>
  </body>
</html>

?我要說話

test.js是一個空文件,等待我們去編寫用例,在其中加上:?我要說話

1
2
3
4
5
6
7
8
9
var assert = chai.assert;
describe('Array', function() {
    describe('#indexOf()', function() {
        it('should return -1 when the value is not present', function() {
            assert.equal(-1, [1,2,3].indexOf(5));
            assert.equal(-1, [1,2,3].indexOf(0));
        });
    });
});

?我要說話

在瀏覽器中打開index.html,可以看到用例執行報告:?我要說話

mocha命令參數

mocha命令的基本格式是:mocha [debug] [options] [files]options包括下面這些,我翻譯了一部分目前能理解的?我要說話

1
-h, --help                              輸出幫助信息    
-V, --version                           輸出mucha版本    
-A, --async-only                        強制讓所有測試用例必須使用callback或者返回promise的方式來異步判斷正確性    
-c, --colors                            啟用報告中顏色    
-C, --no-colors                         禁用報告中顏色    
-G, --growl                             enable growl notification support    
-O, --reporter-options <k=v,k2=v2,...>  reporter-specific options    
-R, --reporter <name>                   specify the reporter to use    
-S, --sort                              排序測試文件    
-b, --bail                              bail after first test failure    
-d, --debug                             enable node's debugger, synonym for node --debug
-g, --grep <pattern>                    只執行滿足 <pattern>格式的用例    
-f, --fgrep <string>                    只執行含有 <string> 的用例    
-gc, --expose-gc                        展示gc回收的log    
-i, --invert                            讓 --grep 和 --fgrep 的匹配取反    
-r, --require <name>                    require一下<name>指定的模塊    
-s, --slow <ms>                         指定slow時間(單位ms,默認75ms)    
-t, --timeout <ms>                      指定超時時間(單位ms,默認2000ms)    
-u, --ui <name>                         指定user-interface (bdd|tdd|exports)    
-w, --watch                             觀察用例文件變化,并重新執行    
--check-leaks                           檢測未回收global變量泄露    
--compilers <ext>:<module>,...          用指定的模塊來編譯文件    
--debug-brk                             啟用node的debug模式    
--delay                                 等待異步的用例集(見前邊的)    
--es_staging                            enable all staged features    
--full-trace                            display the full stack trace    
--globals <names>                       allow the given comma-delimited global [names]    
--harmony                               enable all harmony features (except typeof)    
--harmony-collections                   enable harmony collections (sets, maps, and weak maps)    
--harmony-generators                    enable harmony generators    
--harmony-proxies                       enable harmony proxies    
--harmony_arrow_functions               enable "harmony arrow functions" (iojs)    
--harmony_classes                       enable "harmony classes" (iojs)    
--harmony_proxies                       enable "harmony proxies" (iojs)    
--harmony_shipping                      enable all shipped harmony features (iojs)    
--inline-diffs                          顯示預期和實際結果的string差異比較    
--interfaces                            display available interfaces    
--no-deprecation                        silence deprecation warnings    
--no-exit                               require a clean shutdown of the event loop: mocha will not call process.exit    
--no-timeouts                           禁用timeout,可通過--debug隱式指定    
--opts <path>                           定義option文件路徑    
--prof                                  顯示統計信息    
--recursive                             包含子目錄    
--reporters                             展示可用報告    
--retries                               設置失敗用例重試次數    
--throw-deprecation                     每次調用deprecated函數的時候都拋出一個異常    
--trace                                 顯示函數調用棧    
--trace-deprecation                     啟用的時候顯示調用棧    
--watch-extensions <ext>,...            --watch監控的擴展

?我要說話

下面是官方文檔對部分命令的詳細說明:?我要說話

用例一旦更新立即執行?我要說話

例如--compilers coffee:coffee-script編譯CoffeeScript 1.6,或者--compilers coffee:coffee-script/register編譯CoffeeScript 1.7+?我要說話

如果只對第一個拋出的異常感興趣,可以使用此命令。?我要說話

開啟nodejs的debug模式,可以在debugger語句處暫停執行。?我要說話

names是一個以逗號分隔的列表,如果你的模塊需要暴露出一些全局的變量,可以使用此命令,例如mocha --globals app,YUI。這個命令還可以接受通配符,例如--globals '*bar。參數傳入 * 的話,會忽略所有全局變量。?我要說話

默認情況下,mocha并不會去檢查應用暴露出來的全局變量,加上這個配置后就會去檢查,此時某全局變量如果沒有用上面的–GLOBALS去配置為可接受,mocha就會報錯?我要說話

這個命令可以用來引入一些依賴的模塊,比如should.js等,這個命令相當于在測試目錄下每個js文件頭部運行一下require('should.js'),模塊中對Object、Array等對象的擴展會生效,也可以用--require ./test/helper.js這樣的命令去引入指定的本地模塊。但是… 很雞肋的是,如果要引用模塊導出的對象,還是需要require,var should = require('should')這樣搞。?我要說話

–ui選項可以用來指定所使用的測試接口,默認是“bdd”?我要說話

這個命令可以用來指定報告格式,默認是“spec”??梢允褂玫谌降膱蟾鏄邮?,例如:npm install mocha-lcov-reporter,--reporter mocha-lcov-reporter?我要說話

用來指定用例超時時間?我要說話

用來指定慢用例判定時間,默認是75ms?我要說話

grep pattern可以用來篩選要執行的用例或用例集,pattern參數在mocha內部會被編譯成一個正則表達式。假如有下面的測試用例:?我要說話

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
describe('api', function() {
  describe('GET /api/users', function() {
    it('respond with an array of users', function() {
      // ...
    });
  });
});

describe('app', function() {
  describe('GET /users', function() {
    it('respond with an array of users', function() {
      // ...
    });
  });
});

?我要說話

可以用--grep api、--grep app、--grep users、--grep GET,來篩選出要執行的用例。?我要說話

測試接口類型

mocha的測試接口類型指的是集中測試用例組織模式的選擇,包括BDD行為驅動開發(Behavior Driven Development),TDD測試驅動開發(Test-Driven Development),Exports,QUnit 和 Require-style 幾種。?我要說話

BDD

BDD測試接口提供 describe(), context(), it(), specify(), before(), after(), beforeEach(), 和 afterEach()幾種函數,其中context函數只是describe函數的別名,specify函數也是if函數的別名。mocha默認的測試接口,前邊的所有例子都是基于BDD來編寫的。?我要說話

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
describe('Array', function() {
   before(function() {
     // ...
   });

   describe('#indexOf()', function() {
     context('when not present', function() {
       it('should not throw an error', function() {
         (function() {
           [1,2,3].indexOf(4);
         }).should.not.throw();
       });
       it('should return -1', function() {
         [1,2,3].indexOf(4).should.equal(-1);
       });
     });
     context('when present', function() {
       it('should return the index where the element first appears in the array', function() {
         [1,2,3].indexOf(3).should.equal(2);
       });
     });
   });
 });

?我要說話

TDD

TDD接口提供 suite(), test(), suiteSetup(), suiteTeardown(), setup(), 和 teardown()函數,用例寫法如下:?我要說話

1
2
3
4
5
6
7
8
9
10
11
suite('Array', function() {
  setup(function() {
    // ...
  });

  suite('#indexOf()', function() {
    test('should return -1 when not present', function() {
      assert.equal(-1, [1,2,3].indexOf(4));
    });
  });
});

?我要說話

EXPORTS

Exports 的寫法有的類似于Mocha的前身expresso,其寫法如下:?我要說話

1
2
3
4
5
6
7
8
9
10
11
12
13
module.exports = {
  before: function() {
    // ...
  },

  'Array': {
    '#indexOf()': {
      'should return -1 when not present': function() {
        [1,2,3].indexOf(4).should.equal(-1);
      }
    }
  }
};

?我要說話

通過exports導出的對象里邊,除了幾個鉤子函數之外,其他的Object類型屬性都是用例集,function類型的屬性都是用例。?我要說話

QUNIT

像TDD接口一樣支持suite和test函數,同時又像BDD一樣支持before(), after(), beforeEach(), 和 afterEach(),等鉤子函數。?我要說話

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function ok(expr, msg) {
  if (!expr) throw new Error(msg);
}

suite('Array');

test('#length', function() {
  var arr = [1,2,3];
  ok(arr.length == 3);
});

test('#indexOf()', function() {
  var arr = [1,2,3];
  ok(arr.indexOf(1) == 0);
  ok(arr.indexOf(2) == 1);
  ok(arr.indexOf(3) == 2);
});

suite('String');

test('#length', function() {
  ok('foo'.length == 3);
});

?我要說話

REQUIRE

require測試接口允許你通過require來導入describe函數,取個任意的別名。如果你不希望測試中出現全局的變量,這個接口也是十分有用的。值得注意的是,這里的require不能直接通過node命令來執行,node的模塊管理是不能解析這里的require的,需要通過mocha命令來運行。?我要說話

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ar testCase = require('mocha').describe;
var pre = require('mocha').before;
var assertions = require('mocha').it;
var assert = require('chai').assert;

testCase('Array', function() {
  pre(function() {
    // ...
  });

  testCase('#indexOf()', function() {
    assertions('should return -1 when not present', function() {
      assert.equal([1,2,3].indexOf(4), -1);
    });
  });
});

?我要說話

測試報告視圖

如果不自己加上自定義的報告輸出,mocha會在控制臺中輸出報告。?我要說話

SPEC視圖

這個是默認的報告樣式,輸出一個嵌套的分級視圖?我要說話

DOT MATRIX視圖

用一系列點點來表示用例,測試的是紅點,未實現的是藍點,比較慢的是黃點,通過的是白點,如果你想讓報告看起來簡潔一些,可以用這個視圖。?我要說話

NYAN視圖

尼瑪這是個毛線視圖啊,官方文檔都懶得給出說明?我要說話

TAP視圖

?我要說話

LANDING STRIP視圖

‘Landing Strip’的意思是飛機降落跑道,這是一個逗逼測試人員弄出來的,像一架飛機降落一樣的視圖。這個是墜機了的視圖……?我要說話

LIST視圖

一個簡單的列表視圖?我要說話

PROGRESS視圖

包含一個簡單的進度條的視圖?我要說話

JSON視圖

輸出一個JSON作為測試結果?我要說話

##JSON STREAM視圖輸出的也是一個JSON,只不過輸出的時候會帶上換行?我要說話

JSONCOV覆蓋率報告

一個依賴 node-jscoverage 模塊生成的視圖,用來生成覆蓋率報告?我要說話

HTMLCOV覆蓋率報告

用來生成一個覆蓋率的html報告https://github.com/expressjs/express/commit/b6ee5fafd0d6c79cf7df5560cb324ebee4fe3a7f?我要說話

MIN視圖

只顯示總體測試情況?我要說話

DOC視圖

生成一個只包含html的body部分的報告,結構如下:例如,測試代碼如下:?我要說話

1
2
3
4
5
6
7
8
describe('Array', function() {
  describe('#indexOf()', function() {
    it('should return -1 when the value is not present', function() {
      [1,2,3].indexOf(5).should.equal(-1);
      [1,2,3].indexOf(0).should.equal(-1);
    });
  });
});

?我要說話

運行命令mocha --reporter doc array,結果:?我要說話

1
2
3
4
5
6
7
8
9
10
11
12
13
<section class="suite">
  <h1>Array</h1>
  <dl>
    <section class="suite">
      <h1>#indexOf()</h1>
      <dl>
      <dt>should return -1 when the value is not present</dt>
      <dd><pre><code>[1,2,3].indexOf(5).should.equal(-1);
[1,2,3].indexOf(0).should.equal(-1);</code></pre></dd>
      </dl>
    </section>
  </dl>
</section>

?我要說話

自己添加head、html等標簽,再加上style,可以生成自定義樣式的報告。?我要說話

MARKDOWN視圖

生成一個markdown版本的報告,例子:https://github.com/senchalabs/connect/blob/90a725343c2945aaee637e799b1cd11e065b2bff/tests.md?我要說話

HTML視圖

目前只有在瀏覽器中運行的mocha才能直接生成html報告,nodejs中可以通過doc視圖或者markdown視圖得到的內容自己用腳本生成html版本的~?我要說話

第三方報告生成器

mocha允許我們自己定義第三方的報告生成器,可以參考文檔。一個例子:TeamCity reporter?我要說話

在瀏覽器中運行mocha

mocha項目下都會有mocha.js和mocha.css供瀏覽器中的測試使用?我要說話

只能在瀏覽器中使用的函數

mocha.allowUncaught() ,未捕獲的錯誤不會被拋出下面是一個例子,在加載測試腳本之前,用mocha.setup('bdd')函數把測試模式設置為BDD接口,測試腳本加載完之后用mocha.run()函數來運行測試?我要說話

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<html>
<head>
  <meta charset="utf-8">
  <title>Mocha Tests</title>
  <link href="https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.css" rel="stylesheet" />
</head>
<body>
  <div id="mocha"></div>

  <script src="https://cdn.rawgit.com/jquery/jquery/2.1.4/dist/jquery.min.js"></script>
  <script src="https://cdn.rawgit.com/Automattic/expect.js/0.3.1/index.js"></script>
  <script src="https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.js"></script>

  <script>mocha.setup('bdd')</script>
  <script src="test.array.js"></script>
  <script src="test.object.js"></script>
  <script src="test.xhr.js"></script>
  <script>
    mocha.checkLeaks();
    mocha.globals(['jQuery']);
    mocha.run();
  </script>
</body>
</html>

?我要說話

GREP

瀏覽器中可以通過在url后邊加?grep=api參數,來使用grep命令?我要說話

瀏覽器中的mocha配置

可以通過mocha.setup()命令來設置配置?我要說話

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Use "tdd" interface.  This is a shortcut to setting the interface;
// any other options must be passed via an object.
mocha.setup('tdd');

// This is equivalent to the above.
mocha.setup({
  ui: 'tdd'
});

// Use "tdd" interface, ignore leaks, and force all tests to be asynchronous
mocha.setup({
  ui: 'tdd',
  ignoreLeaks: true,
  asyncOnly: true
});

?我要說話

瀏覽器中特有的選項

如果被設置為true,mocha不會嘗試用高亮語法輸出測試代碼?我要說話

在服務端運行的時候,mocha會去加載test目錄下的mocha.opts文件,來讀取mocha配置項。這個配置文件中的每一行代表一項配置。如果運行mocha命令的時候,帶上的配置參數與這個配置文件中的配置沖突的話,以命令中的為準。例如: –require should –reporter dot –ui bdd上面的配置就會讓mocha 引入一下should模塊、報告樣式設置為dot,并且使用bdd的測試接口。?我要說話

test/ 目錄

默認情況下,mocha會去當前路徑下的去找 ./test/*.js或者./test/*.coffee當做測試文件,所以測試文件應該放在test目錄下?我要說話

編輯器插件

TEXTMATE、JETBRAINS (IntelliJ IDEA, WebStorm, etc.) 等編輯器均有支持mocha測試的插件,需要使用的話請自行查閱。

原文轉自: http://www.zoucz.com/blog/2016/08/02/nodejs-unittest-mochajs/

国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97