Try T.M Engineer Blog

多摩市で生息するエンジニアが「アウトプットする事は大事だ」と思って始めたブログ

Jestについて勉強してみた

最近、Jestを使ってテストコードを頻繁に書き始めたので、自身の使い方整理のため、以下雑にメモを残しておく。

test('null', () => {
  const n = null;
  expect(n).toBeNull(); // null一致
  expect(n).toBeDefined(); // undefined以外一致
  expect(n).not.toBeUndefined(); // undefinedではない
  expect(n).not.toBeTruthy(); // Truthyではない
  expect(n).toBeFalsy(); // Falsyである
});

test('0', () => {
  const z = 0;
  expect(z).not.toBeNull(); // nullではない
  expect(z).toBeDefined(); // undefined以外一致
  expect(z).not.toBeUndefined(); // undefinedではない
  expect(z).not.toBeTruthy(); // Truthyではない
  expect(z).toBeFalsy(); // Falsyである
});

test('数値', () => {
  const value = 2 + 2;
  expect(value).toBeGreaterThan(3); // value > 3
  expect(value).toBeGreaterThanOrEqual(3.5); // value >= 3.5
  expect(value).toBeLessThan(5); // value < 5
  expect(value).toBeLessThanOrEqual(4.5); // value <= 4.5
  expect(value).toBe(4); // value = 4
  expect(value).toEqual(4); // value = 4
});

test('浮動小数点', () => {
  const value = 0.1 - 0.2;
  //expect(value).toBe(-0.1);         このように書くと、丸め込み誤差が原因で期待通りに動作しない
  expect(value).toBeCloseTo(-0.1); // これならば正しく動く
});

test('正規表現', () => {
  expect('team').not.toMatch(/I/); // 正規表現に一致しない
  expect('Christoph').toMatch(/stop/); // 正規表現に一致
});

test('配列', () => {
  const shoppingList = [
    'diapers',
    'kleenex',
    'trash bags',
    'paper towels',
    'milk',
  ];
  
  expect(shoppingList).toContain('milk'); // 配列に含まれているか
  expect(new Set(shoppingList)).toContain('milk'); // Setに含まれているか
});

test('例外', () => {
  function compileAndroidCode() {
    throw new Error('you are using the wrong JDK');
  }

  expect(() => compileAndroidCode()).toThrow(); // throwされているか
  expect(() => compileAndroidCode()).toThrow(Error); // throwのErrorが呼ばれているか
  expect(() => compileAndroidCode()).toThrow('you are using the wrong JDK'); // messageの一致
  expect(() => compileAndroidCode()).toThrow(/JDK/); // 正規表現も可
});

test('予定', () => {
  test.todo('テストの予定を立てれる')
})

test('モック', () => {
  // モック関数を作る
  const mockFn = jest.fn()
  mockFn
    .mockImplementationOnce( () => {
      return '返り値1'
    })
    .mockImplementationOnce( () => {
      return '返り値2'
    })
    .mockImplementationOnce( () => {
      return '返り値3'
    })

  expect(mockFn()).toBe('返り値1') // 1回目の呼び出し
  expect(mockFn()).toBe('返り値2') // 2回目の呼び出し
  expect(mockFn()).toBe('返り値3') // 3回目の呼び出し

  // モックインスタンスを作る
  const mockInsFn = jest.fn()
  mockInsFn
    .mockImplementation( () => {
      return {
        hoge: jest.fn()
          .mockImplementationOnce( () => {
            return 'hoge関数の返り値1'
          })
          .mockImplementationOnce( () => {
            return 'hoge関数の返り値2'
          }),
        fuga: () => {
          return 'fuga関数の返り値'
        }
      }
    })

  const mockIns = new mockInsFn()
  expect(mockIns.hoge()).toBe('hoge関数の返り値1') // hoge関数の1回目の呼び出し
  expect(mockIns.hoge()).toBe('hoge関数の返り値2') // hoge関数の2回目の呼び出し
  expect(mockIns.fuga()).toBe('fuga関数の返り値') // fuga関数の呼び出し
})

// CDKを呼び出す上で必要なものを定義
import { SynthUtils } from '@aws-cdk/assert'
import * as cdk from '@aws-cdk/core'
import { SampleStack } from '../src/deploy/sampleStack'

test('スナップショット', () => {
  const app = new cdk.App()
  const stack = new SampleStack(app, `SampleStack`, {
    stage: 'dev',
    roleName: 'sampleRole'
  })
  expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot()
})

/*
// スナップショットの結果
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`スナップショット 1`] = `
Object {
  "Resources": Object {
    "sampleRole5138FD4B": Object {
      "Properties": Object {
        "AssumeRolePolicyDocument": Object {
          "Statement": Array [
            Object {
              "Action": "sts:AssumeRole",
              "Effect": "Allow",
              "Principal": Object {
                "Service": "lambda.amazonaws.com",
              },
            },
          ],
          "Version": "2012-10-17",
        },
        "ManagedPolicyArns": Array [
          Object {
            "Fn::Join": Array [
              "",
              Array [
                "arn:",
                Object {
                  "Ref": "AWS::Partition",
                },
                ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
              ],
            ],
          },
        ],
        "RoleName": "sampleRole",
      },
      "Type": "AWS::IAM::Role",
    },
  },
}
`;
*/

感想

Jest は使ってみた感じ、むちゃくちゃ便利だ。

とにかくモックが簡単で、関数やインスタンスが作りやすい印象を受けた。

スナップショットも取得することができ、イマドキ感も感じる。

試しに、CDKのCloudFormationテンプレートのスナップショットを取得してみたが、これは使えそうだ。個人的には、CloudFromationのテンプレートテストはそこまで必要性を感じていないのだが、スナップショット残しておくだけでも、差分確認ができるという点で便利だと思えた。

まだまだ使い慣れていないので、ドキュメントを読みつつ、さらなる使い方を学んでいきたい。