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

## はじめに

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

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

1. [jest.fn()](https://qiita.com/only/items/466a09c8602466fe2333)　
2. [jest.SpyOn()](https://qiita.com/only/items/e3c46e6155dc44644111)
3. jest.mock()　←今回はこいつについて

こいつらは似たようなことができるので  
どれを使えばいいのかいつもわからなくなります。  
今回はjest.mock()について説明していきます。

## jest.mock()
- ライブラリや外部依存関係をmock化するために使用されます。
- jest.mock()を使用することでモジュール全体をmockに置き換えることができる。

テスト対象の関数
```typescript
import moment from 'moment'  
  
export function mockTest() {  
    const result = moment().format()  
    return result  
}
```
上記の関数はmomentライブラリを使用して現在の日付をyyyy-mm-ddの形式で
取得する関数です。


テスト
```typescript
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に置き換えて親コンポーネントだけでの
　単体テストを行うこともできます。


テスト対象のコンポーネント
```typescript
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>  
		)
}
```

　↓依存している子コンポーネントです。
```typescript
import React from 'react';  
  
type Props = {  
	name:string,  
	age:number,  
	}
export function PeopleInfo({name, age}:Props) {  
	return (  
			<div>
			{`${name}さんは${age}歳です。`}
			</div>
			)
}

```

Parentコンポーネントのテスト
```typescript
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は全てまとめて一つの引数になるので２つ目の[]の値は
	0固定になります。




###  最後に
Jestについての理解がだいぶ深まりました。
うまく使いこなして開発スピードをアップさせていきたいと思います。