確認したバージョン
node v6.9.1
mocha @3.1.2
sinon @1.17.6
describeとitの処理順番
次の様なテストを書いたとします。
ログはどの様に出力されるでしょうか?
// test.js
describe('doHogeのテスト', () => {
    console.log(0);
    describe('fugaな時', () => {
        console.log(1);
        it('fooはpiyoになる', () => {
            console.log(2);
        });
        console.log(3);
        it('barはpiyoyoになる', () => {
            console.log(4);
        });
        console.log(5);
    });
    console.log(6);
});
 
 
..
 
 
 
....
 
 
 
.......
 
 
  でっでー
でっでー
 
before, after などをよく使う方は分かったかと思うのですが、
0,1,2,3,4,5,6 とは出ません。
実際に動かした結果がこちら
$ $(npm bin)/mocha test.js 
0
1
3
5
6
  doHogeのテスト
    fugaな時
2
      ✓ fooはpiyoになる
4
      ✓ barはpiyoyoになる
  2 passing (7ms)
itの外側にある0,1,3,5,6が先に出力され、it内にある2,4が後から出力されています。
なぜ?
before, afterなどの処理のためだと思われます。(mocha内を読もうとしたのですが挫折しましたorz)
mochaはdescribeを実行し、途中で呼ばれた before, beforeEach, it, afterEach, afterを拾って貯めていき、その後describe毎に、
- before
- beforeEach
- it
- afterEach
- beforeEach
- it
- afterEach
- ...(itの分だけ繰り返し)
 
- after
という順番で呼び出されます。
itだけ使っている忘れがちですが、こんな理由でitの外と中で処理順番が変わってくるのです。
気をつけたいこと with Sinon (本題)
ここにはまった
前置きが長くなりましたが、ここが伝えたかったところです。結構はまったのでw
私はうっかりこんなテストを書きました。
// test.js
import sinon      from 'sinon';
import { expect } from 'chai';
import * as funcs from './functions'; // テスト対象
import * as utils from './utils';     // テスト対象の内部で呼ばれるやつ
describe('doHogeのテスト', () => {
    describe('fugaな時', () => {
        // 呼ばれる(1)
        // テスト対象のfuncs.doHogeの内部で呼ばれるutils.isHogeをスタブ化
        const stub = sinon.stub(utils, 'isHoge', () => true);
        it('fooはpiyoになる', () => {
            // 呼ばれる(3)
            const actual = funcs.doHoge('foo', 'fuga');
            expect(actual).to.equal('piyo');
        });
        it('barはpiyoyoになる', () => {
            // 呼ばれる(4)
            const actual = funcs.doHoge('bar', 'fuga');
            expect(actual).to.equal('piyoyo');
        });
        // 呼ばれる(2)
        // スタブ解除
        stub.restore();
    });
});
前述の通り、実際のテストが実行されるitのコールバック(第二引数)は一度収集され、のちの実行されます。
ですので、テストのためにスタブ化したisHogeは、テストが実行される前にrestoreされてしまっているのです。
回避の例:関数化してit内から呼ぶ
before, afetrを使っても良いと思うのですが、ここではスタブ化処理を関数に入れたものを紹介します。
describe('doHogeのテスト', () => {
    describe('fugaな時', () => {
        // スタブ化->テスト対象の実行→スタブ化の解除をまとめた関数
        // これをitの中から呼ぶことで、it毎に確実にスタブ化+解除が実行される
        const exec = (input) => {
            // テスト対象のfuncs.doHogeの内部で呼ばれるutils.isHogeをスタブ化
            const stub = sinon.stub(utils, 'isHoge', () => true);
            const actual = funcs.doHoge(input, 'fuga');
            // スタブ解除
            stub.restore();
            return actual;
        };
        it('fooはpiyoになる', () => {
            const actual = exec('foo');
            expect(actual).to.equal('piyo');
        });
        it('barはpiyoyoになる', () => {
            const actual = exec('bar');
            expect(actual).to.equal('piyoyo');
        });
    });
});
スタブ化したい内容や、テストしたい値の受け取り方次第で書き方も変わるので、実行順番に気をつけて、都度最適化する必要がありますが、1例として参考にしていただければ幸いです。
まとめ
上から書いた順に実行されないから気をつけて!!!
## おまけ 説明しやすいようにサンプルコードを書きましたが、本当に書くときはループで書くとよいですよ。
describe('doHogeのテスト', () => {
    // テスト条件をオブジェクトにまとめる
    const dataProvider = {
        'fugaな時、fooはpiyoになる' : {
            data : {
                isHoge : true,
                arg1   : 'foo',
                arg2   : 'fuga',
            },
            expected : 'piyo',
        },
        'fugaな時、barはpiyoyoになる', () => {
            data : {
                isHoge : true,
                arg1   : 'bar',
                arg2   : 'fuga',
            },
            expected : 'piyo',
        },
    };
    // 実行部
    const exec = (data) => {
        // テスト対象のfuncs.doHogeの内部で呼ばれるutils.isHogeをスタブ化
        const stub = sinon.stub(utils, 'isHoge', () => data.isHoge);
        const actual = funcs.doHoge(data.arg1, data.arg2);
        // スタブ解除
        stub.restore();
        return actual;
    };
    // ガンガン回してテスト
    for (let [describe, testCase] of Object.entries(dataProvider)) {
        it(describe, () => {
            const actual = exec(testCase.data);
            expect(actual).to.equal(testCase.expected);
        });
    };
});
