実際にのぞいてみる
実際にモック関数をつくって、コンソールに出して、中身を見てみようと思います。
describe("jest.fn()",()=>{
it("jest.fn()の中身を見てみる",()=>{
const mockFunction = jest.fn();
console.log({mockFunction});
})
})
{
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を使います。
describe("jest.fn()",()=>{
it("mockReturnValue",()=>{
const mockFunction = jest.fn().mockReturnValue("Hello mock")
console.log(mockFunction())
})
})
Hello mock
mockResolveValue
mock関数の返り値として、Promiseを設定したい場合はmockResolvedValueを使います。
describe("jest.fn()",()=>{
it("mockResolvedValue",async()=>{
const mockFunction = jest.fn().mockResolvedValue("Hello mock")
console.log(mockFunction())
})
})
Promise { 'Hello mock' }
mockRejectValue
mock関数の帰りとして、エラーを返したい場合はmockRejectedValueを使います。
describe("jest.fn()",()=>{
it("mockRejectedValue",async()=>{
const mockFunction = jest.fn().mockRejectedValue(new Error("Hello mock"))
console.log(mockFunction())
})
})
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関数を実行する際の関数とすることができます。
describe("jest.fn()",()=>{
it(".mockImplementation()",()=>{
const mockFunction = jest.fn().mockImplementation(()=>"Hello mock")
console.log(mockFunction());
})
})
Hello mock
mock~Once
mockReturnValue、mockResolveValue、mockRejectValue、mockImplementationには、それぞれOnceという関数も用意されており、それぞれ1度のみ有効な返り値を設定することができます。
今回は例としてmockImplementationOnceを使ってみたいと思います。
その名の通り、1度のみ有効なmock関数の返り値を設定します。mock関数を呼び出す度に返り値を変更したい場合に用います。
下の例では、1回目の呼び出しでHello mock
、2回目の呼び出しでこんにちは モック
をreturnする関数を渡しており、それぞれ1回ずつ呼び出されているのがわかります。3回目の呼び出しは設定していないため、undefined
となっているのも確認できますね。
describe("jest.fn()",()=>{
it(".mockImplementationOnce()",()=>{
const mockFunction = jest.fn().mockImplementationOnce(()=>"Hello mock").mockImplementationOnce(()=>"こんにちは モック")
console.log(mockFunction());
console.log(mockFunction());
console.log(mockFunction());
})
})
Hello mock
こんにちは モック
undefined
mockプロパティ
mock
mock関数にはmockというプロパティがあり、その中にそれぞれcalls
,contexts
,instances
,invocationCallOrder
,results
というプロパティが配列が入っていて、mock関数がどのように呼ばれてきたか
を記録してくれています。
describe("jest.fn()",()=>{
it("mock",()=>{
console.log(jest.fn().mock);
})
})
{
calls: [],
contexts: [],
instances: [],
invocationCallOrder: [],
results: []
}
mock.calls
mock関数がどのような引数で実行されてきたかが記録されています。
mock関数を異なる引数で2回呼び出すと。1回目の呼び出し時の引数が配列の先頭要素に、2回目の呼び出しがその次に入っていることが確認できます。
それぞれの呼び出し時の引数も配列として格納されていますね。
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);
})
})
{
Hello mock
こんにちは モック
[
[ 'Hello', ' mock' ],
[ 'こんにちは', 'モック' ]
]
}
mock.results
mock関数が実行された結果を記録しています。callsが渡された引数で、resultsが結果です。
typeにはreturn
,throw
,incomplete
の三種類があり、それぞれ以下の意味を持っています。
- return:正常終了
- throw:値を返して呼び出しが完了
- incomplete:呼び出しがまだ完了していない
下の例で確認してみると、callsと違い、呼び出しごとの要素はオブジェクトで格納されているようですね。
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);
})
})
{
Hello mock
こんにちは モック
[
{ type: 'return', value: 'Hello mock' },
{ type: 'return', value: 'こんにちは モック' }
]
}
mock.instances
インスタンス化されたmock関数では、instances配列の中にインスタンスの情報を持っているようです。
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);
})
})
true
true
mock.contexts
呼び出し毎のコンテキストの情報を持っているようです。
describe("jest.fn()",()=>{
it("mock.contexts",()=>{
const mockFunction = jest.fn()
const b= {}
const bound = mockFunction.bind(b);
bound()
console.log(mockFunction. mock.contexts)
})
})
[
{}
]
mock.lastCall
mock関数を1度でも呼び出すと、mockプロパティ内にlastCallというプロパティが新たに確認できます。
これは最後にmock関数が呼ばれた際にmock関数に渡された引数の配列を持っています。
下の例では2回目に渡されたこんにちは
、モック
がlastCallに入っているのが確認できますね。
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);
})
})
{
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関数はリセットされません。
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());
})
})
})
{
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になっていますね。
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());
})
})
{
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を書く時に紹介したいと思います。