EY-Office ブログ

テストコードのbeforeEachはitに対してでdescribeではないのか

私も多くのソフトウェア開発者と同様に🦁ライオンの人にインスパイアされ、20年近くもテストを書いています。
RSpecも初期の頃から使っていますが、最近になって大きな勘違いをしていた事に気がつきました。

beforeEach in test code is for it and not describe Stable Diffusion(mage.space)が生成した画像です

順をおって説明

1. 最初

テスト対象の関数は、少ないパラメーターを受け取り複数のテーブルにレコードを作成します。テストとしては複数のテーブルに作成されたレコードを検証することなので、以下のようなテストを書きました。テスティングフレームワークはMocha です。

describe('ある関数のテスト', () => {
  beforeEach(() => {
    // 1. いろいろと準備
    // 2. パラメータAで、ある関数を呼び出す
  });

  it('テーブル1が作成されている', () => {
    // 作成されたレコードの検証
  });

  it('テーブル2が作成されている', () => {
    // 作成されたレコードの検証
  });

  // ・・・
});

2. テストの高速化

1.では it()の実行毎にある関数を呼び出されます。いろいろとあってある関数の実行が遅いのでbeforeEach()before()に置き換えました(RSpecでいうとbefore(:all)です)。こうするとある関数の実行は1回になりテストが高速されます。

ちなみにit()内は、テーブルを読み出すだけで副作用がないので、問題は起きません。

describe('ある関数のテスト', () => {
  before(() => {
    // 1. いろいろと準備
    // 2. パラメータAで、ある関数を呼び出す
  });

  it('テーブル1が作成されている', () => {
    // 作成されたレコードの検証
  });

  it('テーブル2が作成されている', () => {
    // 作成されたレコードの検証
  });

  // ・・・
});

3. テストケースの追加

ある関数を別のパラメーターで実行するテストケースを追加しました。

describe('ある関数のテスト', () => {
  describe('パラメータAでテスト', () => {
    before(() => {
      // 1. いろいろと準備
      // 2. パラメータBで、ある関数を呼び出す
    });

    it('テーブル1が作成されている', () => {
      // 作成されたレコードの検証
    });

    it('テーブル2が作成されている', () => {
      // 作成されたレコードの検証
    });

    // ・・・
  });

  describe('パラメータBでテスト', () => {
    before(() => {
      // 1. いろいろと準備
      // 2. パラメータBで、ある関数を呼び出す
    });

    it('テーブル1が作成されている', () => {
      // 作成されたレコードの検証
    });

    it('テーブル2が作成されている', () => {
      // 作成されたレコードの検証
    });

    // ・・・
  });
});

4. いろいろと準備を共通化

これから他のパラメーターのテストも追加されるの、いろいろと準備を共通化しました。

しかしテストが失敗してしまいました。😅

describe('ある関数のテスト', () => {
  beforeEach(() => {
    // いろいろと準備
  });

  describe('パラメータAでテスト', () => {
    before(() => {
       // パラメータBで、ある関数を呼び出す
    });

    it('テーブル1が作成されている', () => {
      // 作成されたレコードの検証
    });

    it('テーブル2が作成されている', () => {
      // 作成されたレコードの検証
    });

    // ・・・
  });

  describe('パラメータBでテスト', () => {
    before(() => {
      // パラメータBで、ある関数を呼び出す
    });

    it('テーブル1が作成されている', () => {
      // 作成されたレコードの検証
    });

    it('テーブル2が作成されている', () => {
      // 作成されたレコードの検証
    });

    // ・・・
  });
});

失敗の原因

わかる方は判ると思いますが、いろいろと準備beforeEach()にしたのが原因でしてた。
私はbeforeEach()は、コード上でそれ以降に書かれている describe()の直前で実行されると思っていたのですが、真実はit()の直前で実行されるのです(ドキュメントにも書かれていますね)。

ただし、いろいろと準備の中にはテーブルのクリーンアップも含まれているので、ある関数の実行前には必ず実行したいのです。3.に戻すしかないのでしょうか・・・

- about -

EY-Office代表取締役
・プログラマー
吉田裕美の
開発者向けブログ