mockClear()とmockReset()


以前mockClear()mockReset()のリセットされている情報の違いによるハマりがあったので、メモ

それぞれの特徴

以下のような関数をテストするとする

// テストしたい関数
function main(str?: string) {
 user.get();

 // 引数があるときだけもう一度user.getを呼ぶ
 if(str) {
  user.get();
 }
}


// モック対象
function get(){
  // なにか処理
}

mockClear()

mockFn.mock.callsmockFn.mock.instancesの配列に格納されている全ての情報をリセット


const userSpy = jest.spyOn(user, 'get');
beforeEach(() => {
  userSpy.mockReturnValue(true);
}) ;
afterEach(() => {
  userSpy.mockClear();
})

test('1回呼び出されていることを確認', () => {
  main(); // テスト対象の関数
  userSpy.toHaveBeenCalledTimes(1);
});

test('引数が設定されている場合は2回呼び出されていることを確認', () => {
  main('OK'); // テスト対象の関数(引数あり)
  userSpy.toHaveBeenCalledTimes(2);  
})

もし、afterEach()でmockClear()をしていない場合は、1度目のテストの呼び出し回数を引き継ぐことになってしまうので期待した結果が得られなくなる
テストが実行されるタイミングにもよるけれど、上から順に実行された場合は
userSpyの呼び出し回数は3回になり、テスト失敗になる

mockReset()

mockReset()もmockFn.mock.callsmockFn.mock.instancesの配列に格納されている全ての情報をリセット
さらにモックされた戻り値、実装も削除する

たとえば…

const userSpy = jest.spyOn(user, 'get');
beforeEach(() => {
  userSpy.mockReturnValueOnce(true).mockReturnValueOnce(false); // 1度目呼ばれたときと2度目に呼ばれたときと戻り値が異なる状況にしたい
}) ;
afterEach(() => {
  // userSpy.mockClear();
  userSpy.mockReset();
})

test('1回呼び出されていることを確認', () => {
  main(); // テスト対象の関数
  userSpy.toHaveBeenCalledTimes(1);
  // userSpyの戻り値 1度目…true
});

test('引数にOKが渡されている場合は2回呼び出されていることを確認', () => {
  main('OK');
  userSpy.toHaveBeenCalledTimes(2);
  //  userSpyの戻り値 1度目…true, 2度目…false
})

afterEach()でmockReset()ではなく、mockClear()をしていた場合、テストの実行順も影響するが
1つ目に書かれているテストが実行されたあとに、2つ目のテストが実行されると
userSpyの戻り値は 1度目…false, 2度目…true となり、期待した結果でなくなる
afterEach()でクリアしているつもりだったけれど、mockClear()ではモックされた戻り値・実装は
リセットされないため、上記のような結果になってしまう

リセットされる内容を考慮しながら使う必要がある

おまけのmockRestore()

mockRestore()というのもあって
モック関数を利用していたものを本物のモジュールに戻したい時に便利
jest.spyOn()でモックが作成されたときにのみ使える
jest.fn()でモックしていた場合は、mockRestore()は使えないので注意