4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

jestを完全に理解したい(jest.mock()編)

Last updated at Posted at 2023-06-13

はじめに

jestでメソッドをmockする際、フワッとした知識で使ってたので改めてまとめました。

今回はJest でよく使う以下のmock3兄弟について説明していきます。

  1. jest.fn() 
  2. jest.SpyOn()
  3. 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についての理解がだいぶ深まりました。
うまく使いこなして開発スピードをアップさせていきたいと思います。

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?