Jestを使ったユニットテストで、UUIDなどのランダム生成値を含むテストをする

最近JestとReactの勉強を兼ねてgutenbergにPRいろいろ出してるのですが、その中で「おぉー」ってなったものを一つ。

ランダムな値が混ざる関数

こういう関数があるとします。

import uuid from 'uuid/v4';
export function createNotice( status, content, options = {} ) {
    const {
        id = uuid(),
        isDismissible = true,
    } = options;
    return {
        type: 'CREATE_NOTICE',
        notice: {
            id,
            status,
            content,
            isDismissible,
        },
    };
}

よくよく見ると、notice.idの値がランダム生成になっています。
そのため通常のアサーションではうまくいきません。

describe( 'createNotice', () => {
    const status = 'status';
    const content = <p>element</p>;
    it( 'should return CREATE_NOTICE action when options is empty', () => {
        const result = createNotice( status, content );
        expect( result ).toEqual( {
            type: 'CREATE_NOTICE',
            notice: {
                status,
                content,
                isDismissible: true,
                id: 'string',
            },
        } );
    } );
} );

これだとこういうエラーになる

Summary of all failing tests
 FAIL  editor/test/actions.js
  ● actions › createNotice › should return CREATE_NOTICE action when options is empty

    expect(received).toEqual(expected)

    Expected value to equal:
      {"notice": {"content": <p>element</p>, "id": "string", "isDismissible": true, "status": "status"}, "type": "CREATE_NOTICE"}
    Received:
      {"notice": {"content": <p>element</p>, "id": "2dd9488b-28f0-4b4c-b1d1-536524e04672", "isDismissible": true, "status": "status"}, "type": "CREATE_NOTICE"}

    Difference:

    - Expected
    + Received

      Object {
        "notice": Object {
          "content": <p>
            element
          </p>,
    -     "id": "string",
    +     "id": "2dd9488b-28f0-4b4c-b1d1-536524e04672",
          "isDismissible": true,
          "status": "status",
        },
        "type": "CREATE_NOTICE",
      }

      at Object.<anonymous> (editor/test/actions.js:430:19)
      at process._tickCallback (internal/process/next_tick.js:109:7)

toMatchObject()expect.any()を組み合わせる

こういうケースの場合、JestだとtoMatchObject()expect.any()を組み合わせると良いみたいです。

describe( 'createNotice', () => {
    const status = 'status';
    const content = <p>element</p>;
    it( 'should return CREATE_NOTICE action when options is empty', () => {
        const result = createNotice( status, content );
        expect( result ).toMatchObject( {
            type: 'CREATE_NOTICE',
            notice: {
                status,
                content,
                isDismissible: true,
                id: expect.any( String ),
            },
        } );
    } );
} );

notice.idは少なくともString型の何かが返ってくるので、expect.any( String )とします。

参考:https://github.com/WordPress/gutenberg/pull/3787#pullrequestreview-80768414

Comment