8
5

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.fn()の中身ってどうなっている?-

Last updated at Posted at 2022-06-08

実際にのぞいてみる

実際にモック関数をつくって、コンソールに出して、中身を見てみようと思います。

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)]
      }
    }

なにやら、たくさんある。心が折れそうになりますが、重要なものから1つ1つ見ていきましょう

返り値の設定

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という関数も用意されており、それぞれ1度のみ有効な返り値を設定することができます。
今回は例としてmockImplementationOnceを使ってみたいと思います。
その名の通り、1度のみ有効なmock関数の返り値を設定します。mock関数を呼び出す度に返り値を変更したい場合に用います。
下の例では、1回目の呼び出しでHello mock、2回目の呼び出しでこんにちは モックをreturnする関数を渡しており、それぞれ1回ずつ呼び出されているのがわかります。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関数を異なる引数で2回呼び出すと。1回目の呼び出し時の引数が配列の先頭要素に、2回目の呼び出しがその次に入っていることが確認できます。
それぞれの呼び出し時の引数も配列として格納されていますね。

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関数を1度でも呼び出すと、mockプロパティ内にlastCallというプロパティが新たに確認できます。
これは最後にmock関数が呼ばれた際にmock関数に渡された引数の配列を持っています。
下の例では2回目に渡されたこんにちは 、モックが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にはこれらの設定情報をリセットする関数が用意されています。1つずつ見ていきましょう。

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を書く時に紹介したいと思います。

8
5
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
8
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?