はじめに
jestでメソッドをmockする際、フワッとした知識で使ってたので改めてまとめました。
今回はJest でよく使う以下のmock3兄弟について説明していきます。
- jest.fn()
- jest.SpyOn()
- jest.mock() ←今回はこいつについて
こいつらは似たようなことができるので
どれを使えばいいのかいつもわからなくなります。
今回はjest.mock()について説明していきます。
jest.mock()
- ライブラリや外部依存関係をmock化するために使用されます。
- jest.mock()を使用することでモジュール全体をmockに置き換えることができる。
テスト対象の関数
import moment from 'moment'
export function mockTest() {
const result = moment().format()
return result
}
上記の関数はmomentライブラリを使用して現在の日付をyyyy-mm-ddの形式で
取得する関数です。
テスト
import moment from 'moment';
import {mockTest} from "./component/mockTest";
jest.mock('moment')
describe('moment', () => {
beforeEach(()=>{
(moment as any).mockImplementation(() => ({
format: jest.fn().mockReturnValue('2023-06-11'),
}))
})
test('mock moment', () => {
const result = mockTest()
expect(result).toBe('2023-06-11')
})
test('mock moment2', () => {
(moment as any).mockImplementation(() => ({
format: jest.fn().mockReturnValue('2023-06-15'),
}))
const result = mockTest()
expect(result).toBe('2023-06-15')
})
})
- このテストではmoment自体をmockに置き換えています。
mockにしたmomentに対してmockImplementationで実装の置き換えをしています。
(moment as any はtypescriptだとmomentにmockImplementationがないと怒られるので型変換しています。) - beforeEachでデフォルトの返り値を指定して書くメソッドで返り値を変更したい場合は
上書きしています。
注意点としてjest.mockを使用するとこのテストファイル内全てのテストで使用されるmomentがmockに置き換わるのでこのテストだけ本物の実装を使いたいって時は
テストファイルを分けてください。
より実践的な使い方
- モジュールそのものを置き換えることができるのでReactでテストしたいコンポーネントに依存する子コンポーネントをmockに置き換えて親コンポーネントだけでの
単体テストを行うこともできます。
テスト対象のコンポーネント
import React from 'react';
import {PeopleInfo} from "./PeopleInfo";
export function Parent() {
const people = [
{id: 1, name: 'ken', age: 24},
{id: 2, name: 'taro', age: 55},
{id: 3, name: 'hoge', age: 4},
]
return (
<div>
<p>名前と年齢の一覧です</p>
{
people.map(elem=>(
<PeopleInfo
key={elem.id}
name={elem.name}
age={elem.age}/>
)
}
</div>
)
}
↓依存している子コンポーネントです。
import React from 'react';
type Props = {
name:string,
age:number,
}
export function PeopleInfo({name, age}:Props) {
return (
<div>
{`${name}さんは${age}歳です。`}
</div>
)
}
Parentコンポーネントのテスト
import React from 'react'
import { render, screen } from '@testing-library/react'
import {PeopleInfo} from "component/PeopleInfo";
import {Parent} from "component/Parent";
jest.mock('component/PeopleInfo')
describe('Parentテスト', () => {
it('名前と年齢の一覧です が表示されていること', () => {
// ARRANGE
// ACT
render(<Parent/>)
// ASSERT
expect(screen.getByText('名前と年齢の一覧です')).toBeInTheDocument()
})
it('PeopleInfoコンポーネントを呼び出していること', () => {
// ARRANGE
// ACT
render(<Parent/>)
// ASSERT
expect(PeopleInfo).toHaveBeenCalled()
})
it('PeopleInfoコンポーネントが3回呼び出されていること', () => {
// ARRANGE
// ACT
render(<Parent/>)
// ASSERT
expect(PeopleInfo).toBeCalledTimes(3)
})
it('PeopleInfoコンポーネントが正しい引数で呼び出されていること', () => {
// ARRANGE
const peopleInfo = PeopleInfo as any
// ACT
render(<Parent/>)
// ASSERT
expect(peopleInfo.mock.calls[0][0]).toEqual({name:'ken',age:24,})
expect(peopleInfo.mock.calls[1][0]).toEqual({name:'taro',age:55,})
expect(peopleInfo.mock.calls[2][0]).toEqual({name:'hoge',age:4,})
});
})
peopleInfo.mock.calls[0][0]
この書き方でmockにしたpeopleInfoの1回目に呼ばれた1個目の引数を確認することができます。
コンポーネントの場合、propsは全てまとめて一つの引数になるので2つ目の[]の値は
0固定になります。
最後に
Jestについての理解がだいぶ深まりました。
うまく使いこなして開発スピードをアップさせていきたいと思います。