javascript - จะเปลี่ยนการจำลองการทำงานของ mock แบบเฉพาะในการทดสอบแต่ละตัวได้อย่างไร?
ฉันต้องการเปลี่ยนการใช้งานของการจำลองการพึ่งพาแบบปลอมในแต่ละการทดสอบโดยขยายพฤติกรรมของม็อกเริ่มต้นและเปลี่ยนกลับไปเป็นการใช้งานต้นฉบับเมื่อการทดสอบถัดไปทำงาน
พูดสั้นๆ นี่คือสิ่งที่ฉันพยายามจะทำ
- การจำลองการพึ่งพา
- เปลี่ยน/ขยายการจำลองการทำงานในหนึ่งการทดสอบ
- กลับไปใช้ม็อกต้นฉบับเมื่อการทดสอบครั้งถัดไปทำงาน
ฉันกำลังใช้ Jest เวอร์ชัน 21 อยู่ขณะนี้ นี่คือลักษณะของการทดสอบทั่วไป:
// __mocks__/myModule.js
const myMockedModule = jest.genMockFromModule('../myModule');
myMockedModule.a = jest.fn(() => true);
myMockedModule.b = jest.fn(() => true);
export default myMockedModule;
// __tests__/myTest.js
import myMockedModule from '../myModule';
// Mock myModule
jest.mock('../myModule');
beforeEach(() => {
jest.clearAllMocks();
});
describe('MyTest', () => {
it('should test with default mock', () => {
myMockedModule.a(); // === true
myMockedModule.b(); // === true
});
it('should override myMockedModule.b mock result (and leave the other methods untouched)', () => {
// Extend change mock
myMockedModule.a(); // === true
myMockedModule.b(); // === 'overridden'
// Restore mock to original implementation with no side effects
});
it('should revert back to default myMockedModule mock', () => {
myMockedModule.a(); // === true
myMockedModule.b(); // === true
});
});
นี่คือสิ่งที่ฉันได้ลองทำไปจนถึงตอนนี้:
mockFn.mockImplementationOnce(fn)it('should override myModule.b mock result (and leave the other methods untouched)', () => { myMockedModule.b.mockImplementationOnce(() => 'overridden'); myModule.a(); // === true myModule.b(); // === 'overridden' });ข้อดี
- กลับไปใช้การใช้งานต้นฉบับหลังจากเรียกครั้งแรก
- มันพังหากการทดสอบเรียก
bหลายครั้ง - มันจะไม่กลับไปใช้การทำงานเดิมจนกว่า
bจะไม่ถูกเรียก (รั่วออกไปในการทดสอบถัดไป) jest.doMock(moduleName, factory, options)it('should override myModule.b mock result (and leave the other methods untouched)', () => { jest.doMock('../myModule', () => { return { a: jest.fn(() => true, b: jest.fn(() => 'overridden', } }); myModule.a(); // === true myModule.b(); // === 'overridden' });ข้อดี
- ล้อเลียนซ้ำอย่างชัดเจนในทุกการทดสอบ
- ไม่สามารถกำหนดการจำลองเริ่มต้นสำหรับการทดสอบทั้งหมดได้
- ไม่สามารถขยายการใช้งานเริ่มต้น จึงบังคับให้ต้องประกาศซ้ำแต่ละเมธอดที่ถูกจำลอง
การจำลองด้วยตนเองด้วยวิธีตั้งค่าตัวแปร (ตามที่อธิบายไว้ที่นี่)
// __mocks__/myModule.js const myMockedModule = jest.genMockFromModule('../myModule'); let a = true; let b = true; myMockedModule.a = jest.fn(() => a); myMockedModule.b = jest.fn(() => b); myMockedModule.__setA = (value) => { a = value }; myMockedModule.__setB = (value) => { b = value }; myMockedModule.__reset = () => { a = true; b = true; }; export default myMockedModule;// __tests__/myTest.js it('should override myModule.b mock result (and leave the other methods untouched)', () => { myModule.__setB('overridden'); myModule.a(); // === true myModule.b(); // === 'overridden' myModule.__reset(); });ข้อดี
- ควบคุมผลลัพธ์จำลองได้อย่างเต็มที่
- โค้ดตัวอย่างจำนวนมาก
- ยากที่จะรักษาในระยะยาว
jest.spyOn(object, methodName)beforeEach(() => { jest.clearAllMocks(); jest.restoreAllMocks(); }); // Mock myModule jest.mock('../myModule'); it('should override myModule.b mock result (and leave the other methods untouched)', () => { const spy = jest.spyOn(myMockedModule, 'b').mockImplementation(() => 'overridden'); myMockedModule.a(); // === true myMockedModule.b(); // === 'overridden' // How to get back to original mocked value? });ข้อเสีย
- ฉันไม่สามารถย้อน
mockImplementationกลับไปยังค่าที่ถูกจำลองเดิมได้ ทำให้ส่งผลต่อการทดสอบต่อไป
- ฉันไม่สามารถย้อน
ข้อเสีย
ข้อเสีย
ข้อเสีย
คำตอบ #1
ใช้ mockFn.mockImplementation(fn).
import { funcToMock } from './somewhere';
jest.mock('./somewhere');
beforeEach(() => {
funcToMock.mockImplementation(() => { /* default implementation */ });
// (funcToMock as jest.Mock)... in TS
});
test('case that needs a different implementation of funcToMock', () => {
funcToMock.mockImplementation(() => { /* implementation specific to this test */ });
// (funcToMock as jest.Mock)... in TS
// ...
});
คำตอบ #2
รูปแบบที่ดีสำหรับการเขียนการทดสอบคือการสร้างฟังก์ชันโรงงานการตั้งค่าที่ส่งคืนข้อมูลที่คุณต้องการสำหรับการทดสอบโมดูลปัจจุบัน
ด้านล่างเป็นตัวอย่างโค้ดตามตัวอย่างที่สองของคุณ แม้ว่าจะอนุญาตให้กำหนดค่ามาตรฐานและค่าที่แทนที่ได้ในลักษณะที่นำกลับมาใช้ซ้ำได้
const spyReturns = returnValue => jest.fn(() => returnValue);
describe("scenario", () => {
beforeEach(() => {
jest.resetModules();
});
const setup = (mockOverrides) => {
const mockedFunctions = {
a: spyReturns(true),
b: spyReturns(true),
...mockOverrides
}
jest.doMock('../myModule', () => mockedFunctions)
return {
mockedModule: require('../myModule')
}
}
it("should return true for module a", () => {
const { mockedModule } = setup();
expect(mockedModule.a()).toEqual(true)
});
it("should return override for module a", () => {
const EXPECTED_VALUE = "override"
const { mockedModule } = setup({ a: spyReturns(EXPECTED_VALUE)});
expect(mockedModule.a()).toEqual(EXPECTED_VALUE)
});
});
เป็นเรื่องสำคัญที่จะบอกว่าคุณ ต้อง รีเซ็ตโมดูลที่ถูกเก็บในแคชโดยใช้ jest.resetModules() ซึ่งสามารถทำได้ใน beforeEach หรือฟังก์ชัน teardown ที่คล้ายกัน
ดูเอกสารวัตถุ jest สำหรับข้อมูลเพิ่มเติม: https://jestjs.io/docs/jest-object.
คำตอบ #3
มาสายนิดหน่อยสำหรับงานปาร์ตี้ แต่ถ้ามีใครคนอื่นมีปัญหากับเรื่องนี้
เราใช้ TypeScript, ES6 และ babel สำหรับการพัฒนา react-native
เรามักจะล้อเลียนโมดูล NPM ภายนอกในไดเรกทอรีหลัก __mocks__
ฉันต้องการแทนที่ฟังก์ชันเฉพาะของโมดูลในคลาส Auth ของ aws-amplify สำหรับการทดสอบเฉพาะ
import { Auth } from 'aws-amplify';
import GetJwtToken from './GetJwtToken';
...
it('When idToken should return "123"', async () => {
const spy = jest.spyOn(Auth, 'currentSession').mockImplementation(() => ({
getIdToken: () => ({
getJwtToken: () => '123',
}),
}));
const result = await GetJwtToken();
expect(result).toBe('123');
spy.mockRestore();
});
สรุป: https://gist.github.com/thomashagstrom/e5bffe6c3e3acec592201b6892226af2
คู่มือ: https://medium.com/p/b4ac52a005d#19c5
