jestをしっかり理解したい -jest.fn()の中身ってどうなっている？- 

# 実際にのぞいてみる
実際にモック関数をつくって、コンソールに出して、中身を見てみようと思います。
```mock.spec.js
describe("jest.fn()",()=>{
  it("jest.fn()の中身を見てみる",()=>{
    const mockFunction = jest.fn();
    console.log({mockFunction});
  })
})
```
```console.log
 {
      mockFunction: [Function: mockConstructor] {
        _isMockFunction: true,
        getMockImplementation: [Function (anonymous)],
        mock: [Getter/Setter],
        mockClear: [Function (anonymous)],
        mockReset: [Function (anonymous)],
        mockRestore: [Function (anonymous)],
        mockReturnValueOnce: [Function (anonymous)],
        mockResolvedValueOnce: [Function (anonymous)],
        mockRejectedValueOnce: [Function (anonymous)],
        mockReturnValue: [Function (anonymous)],
        mockResolvedValue: [Function (anonymous)],
        mockRejectedValue: [Function (anonymous)],
        mockImplementationOnce: [Function (anonymous)],
        mockImplementation: [Function (anonymous)],
        mockReturnThis: [Function (anonymous)],
        mockName: [Function (anonymous)],
        getMockName: [Function (anonymous)]
      }
    }
```
なにやら、たくさんある。心が折れそうになりますが、重要なものから１つ１つ見ていきましょう

## 返り値の設定
### mockReturnValue
mock関数を呼び出した際の返り値を設定する際はmockReturnValueを使います。
```mock.spec.js
describe("jest.fn()",()=>{
  it("mockReturnValue",()=>{
      const mockFunction = jest.fn().mockReturnValue("Hello mock")
      console.log(mockFunction())
   })
})
```
```console.log
Hello mock
```
### mockResolveValue
mock関数の返り値として、Promiseを設定したい場合はmockResolvedValueを使います。
```mock.spec.js
describe("jest.fn()",()=>{
  it("mockResolvedValue",async()=>{
    const mockFunction = jest.fn().mockResolvedValue("Hello mock")
    console.log(mockFunction())
  })
})
```
```console.log
 Promise { 'Hello mock' }
```
### mockRejectValue
mock関数の帰りとして、エラーを返したい場合はmockRejectedValueを使います。
```mock.spec.js
describe("jest.fn()",()=>{
  it("mockRejectedValue",async()=>{
    const mockFunction = jest.fn().mockRejectedValue(new Error("Hello mock"))
    console.log(mockFunction())
  })
})
```
```console.log
    Promise {
      <rejected> Error: Hello mock
          at Object.<anonymous> (/Users/takehiro/development/private/hello-jest/08_mock/mock.test.js:74:54)
          at Promise.then.completed (/Users/takehiro/development/private/hello-jest/node_modules/jest-circus/build/utils.js:316:28)
          at new Promise (<anonymous>)
          at callAsyncCircusFn (/Users/takehiro/development/private/hello-jest/node_modules/jest-circus/build/utils.js:242:10)
          at _callCircusTest (/Users/takehiro/development/private/hello-jest/node_modules/jest-circus/build/run.js:224:40)
          at processTicksAndRejections (internal/process/task_queues.js:97:5)
          at _runTest (/Users/takehiro/development/private/hello-jest/node_modules/jest-circus/build/run.js:156:3)
          at _runTestsForDescribeBlock (/Users/takehiro/development/private/hello-jest/node_modules/jest-circus/build/run.js:67:9)
          at _runTestsForDescribeBlock (/Users/takehiro/development/private/hello-jest/node_modules/jest-circus/build/run.js:61:9)
          at run (/Users/takehiro/development/private/hello-jest/node_modules/jest-circus/build/run.js:25:3)
    }
```
### mockImplementation
mock関数自体に関数を設定したい場合はmockImplementationを使います。
実行したい関数をmockImplementationの引数として渡すことで、mock関数を実行する際の関数とすることができます。
```mock.spec.js
describe("jest.fn()",()=>{
  it(".mockImplementation()",()=>{
    const mockFunction = jest.fn().mockImplementation(()=>"Hello mock")
    console.log(mockFunction());
  })
})
```
```console.log
Hello mock
```
### mock~Once
mockReturnValue、mockResolveValue、mockRejectValue、mockImplementationには、それぞれOnceという関数も用意されており、それぞれ１度のみ有効な返り値を設定することができます。
今回は例としてmockImplementationOnceを使ってみたいと思います。
その名の通り、１度のみ有効なmock関数の返り値を設定します。mock関数を呼び出す度に返り値を変更したい場合に用います。
下の例では、１回目の呼び出しで``Hello mock``、2回目の呼び出しで``こんにちは モック``をreturnする関数を渡しており、それぞれ１回ずつ呼び出されているのがわかります。3回目の呼び出しは設定していないため、``undefined``となっているのも確認できますね。
```mock.spec.js
describe("jest.fn()",()=>{
  it(".mockImplementationOnce()",()=>{
    const mockFunction = jest.fn().mockImplementationOnce(()=>"Hello mock").mockImplementationOnce(()=>"こんにちは モック")
    console.log(mockFunction());
    console.log(mockFunction());
    console.log(mockFunction());
  })
})
```
```console.log
Hello mock
こんにちは モック
undefined
```
## mockプロパティ
### mock
mock関数にはmockというプロパティがあり、その中にそれぞれ``calls``,``contexts``,``instances``,``invocationCallOrder``,``results``というプロパティが配列が入っていて、``mock関数がどのように呼ばれてきたか``を記録してくれています。
```mock.spec.js
describe("jest.fn()",()=>{
  it("mock",()=>{
    console.log(jest.fn().mock);
  })
})
```
```console.log
{
  calls: [],
  contexts: [],
  instances: [],
  invocationCallOrder: [],
  results: []
}
```
#### mock.calls
mock関数がどのような引数で実行されてきたかが記録されています。
mock関数を異なる引数で２回呼び出すと。1回目の呼び出し時の引数が配列の先頭要素に、２回目の呼び出しがその次に入っていることが確認できます。
それぞれの呼び出し時の引数も配列として格納されていますね。
```mock.spec.js
describe("jest.fn()",()=>{
  it("mock.calls",()=>{
    const mockFunction = jest.fn().mockImplementation((text1,text2)=>text1 + text2)
    console.log(mockFunction("Hello ", "mock"))
    console.log(mockFunction("こんにちは ", "モック"))
    console.log(mockFunction.mock.calls);
  })
})
```
```console.log
{
  Hello  mock
  こんにちは モック
  [ 
    [ 'Hello', ' mock' ], 
    [ 'こんにちは', 'モック' ] 
  ]
}
```

#### mock.results
mock関数が実行された結果を記録しています。callsが渡された引数で、resultsが結果です。
typeには``return``,``throw``,``incomplete``の三種類があり、それぞれ以下の意味を持っています。
- return:正常終了
- throw:値を返して呼び出しが完了
- incomplete:呼び出しがまだ完了していない

下の例で確認してみると、callsと違い、呼び出しごとの要素はオブジェクトで格納されているようですね。
```mock.spec.js
describe("jest.fn()",()=>{
  it("mock.results",()=>{
    const mockFunction = jest.fn().mockImplementation((text1,text2)=>text1+text2)
    console.log(mockFunction("Hello ", "mock"))
    console.log( mockFunction("こんにちは ", "モック"))
    console.log(mockFunction.mock.results);
  })
})
```
```console.log
{
  Hello  mock
  こんにちは モック
  [
    { type: 'return', value: 'Hello mock' },
    { type: 'return', value: 'こんにちは モック' }
  ]
}
```
#### mock.instances
インスタンス化されたmock関数では、instances配列の中にインスタンスの情報を持っているようです。
```mock.spec.js
describe("jest.fn()",()=>{
  it("mock.instances",()=>{
    const mockFunction = jest.fn()
    const a = new mockFunction()
    const b = new mockFunction()
    console.log(mockFunction.mock.instances[0] === a);
    console.log(mockFunction.mock.instances[1] === b);
  })
})
```
```console.log
  true
  true
```
#### mock.contexts
呼び出し毎のコンテキストの情報を持っているようです。
```mock.spec.js
describe("jest.fn()",()=>{
  it("mock.contexts",()=>{
    const mockFunction = jest.fn()
    const b= {}
    const bound = mockFunction.bind(b);
    bound()
    console.log(mockFunction. mock.contexts)
  })
})
```
```console.log
 [ 
   {} 
 ]
```

#### mock.lastCall
mock関数を１度でも呼び出すと、mockプロパティ内にlastCallというプロパティが新たに確認できます。
これは最後にmock関数が呼ばれた際にmock関数に渡された引数の配列を持っています。
下の例では２回目に渡された``こんにちは``　、``モック``がlastCallに入っているのが確認できますね。
```mock.spec.js
describe("jest.fn()",()=>{
  it("mock.lastCall",()=>{
    const mockFunction = jest.fn().mockImplementation((text1,text2)=>text1+text2)
    console.log(mockFunction("Hello ", "mock"))
    console.log( mockFunction("こんにちは ", "モック"))
    console.log(mockFunction.mock);
  })
})
```
```console.log
    {
      calls: [ [ 'Hello ', 'mock' ], [ 'こんにちは ', 'モック' ] ],
      contexts: [ undefined, undefined ],
      instances: [ undefined, undefined ],
      invocationCallOrder: [ 1, 2 ],
      results: [
        { type: 'return', value: 'Hello mock' },
        { type: 'return', value: 'こんにちは モック' }
      ],
      lastCall: [ 'こんにちは ', 'モック' ]
    }
```

## mockをリセットする
上までの例ではmock関数に対して返り値をカスタマイズしたり、mockプロパティを読み取ったりしていましたが、mockにはこれらの設定情報をリセットする関数が用意されています。１つずつ見ていきましょう。
### mockClear
mockプロパティをすべてリセットします。``ただし、mock関数はリセットされません。``
```mock.spec.js
describe("jest.fn()",()=>{
  it("mockClear",()=>{
    const mockFunction = jest.fn().mockImplementation((text1,text2)=>text1+text2)
    console.log(mockFunction("Hello ", "mock"))
    console.log( mockFunction("こんにちは ", "モック"))
    console.log(mockFunction.mock, mockFunction.getMockImplementation());
    mockFunction.mockClear()
    console.log(mockFunction.mock, mockFunction.getMockImplementation());
  })
  })
})
```
```console.log
    {
      calls: [ [ 'Hello ', 'mock' ], [ 'こんにちは ', 'モック' ] ],
      contexts: [ undefined, undefined ],
      instances: [ undefined, undefined ],
      invocationCallOrder: [ 1, 2 ],
      results: [
        { type: 'return', value: 'Hello mock' },
        { type: 'return', value: 'こんにちは モック' }
      ],
      lastCall: [ 'こんにちは ', 'モック' ]
    }[Function (anonymous)]

    {
      calls: [],
      contexts: [],
      instances: [],
      invocationCallOrder: [],
      results: []
    }[Function (anonymous)]
```
### mockReset
mockプロパティをすべてリセットします。mockClearと異なり、``mock関数もリセットされます。``
mockResetしたあとはmock関数がundefinedになっていますね。
```mock.spec.js
describe("jest.fn()",()=>{
  it("mockReset",()=>{
    const mockFunction = jest.fn().mockImplementation((text1,text2)=>text1+text2)
    console.log(mockFunction("Hello ", "mock"))
    console.log( mockFunction("こんにちは ", "モック"))
    console.log(mockFunction.mock, mockFunction.getMockImplementation());
    mockFunction.mockReset()
    console.log(mockFunction.mock, mockFunction.getMockImplementation());
  })
})
```
```console.log
    {
      calls: [ [ 'Hello ', 'mock' ], [ 'こんにちは ', 'モック' ] ],
      contexts: [ undefined, undefined ],
      instances: [ undefined, undefined ],
      invocationCallOrder: [ 1, 2 ],
      results: [
        { type: 'return', value: 'Hello mock' },
        { type: 'return', value: 'こんにちは モック' }
      ],
      lastCall: [ 'こんにちは ', 'モック' ]
    }[Function (anonymous)]

    {
      calls: [],
      contexts: [],
      instances: [],
      invocationCallOrder: [],
      results: []
    }undefined
```
### mockRestore
mockRestoreはjest.fn()を使う上ではmockResetと変わりませんが、jest.spyOnというモジュールの特定の関数のみをmockする関数で使うとmock化した関数をオリジナルの関数へ戻すことができます。詳しくは別途jest.spyOnを書く時に紹介したいと思います。