วิธีเปลี่ยนการใช้งานจำลองบนพื้นฐานการทดสอบหนึ่งครั้ง [Jestjs]


96

ฉันต้องการที่จะเปลี่ยนการดำเนินงานของการพึ่งพาล้อเลียนในแต่ละวิธีการทดสอบเดียวโดยการขยายจำลองเริ่มต้นพฤติกรรมและการคืนค่ากลับมาในการดำเนินการเดิมเมื่อรันการทดสอบต่อไป

สั้น ๆ นี่คือสิ่งที่ฉันพยายามบรรลุ:

  1. จำลองการพึ่งพา
  2. เปลี่ยน / ขยายการใช้งานจำลองในการทดสอบเดียว
  3. เปลี่ยนกลับเป็นการจำลองแบบเดิมเมื่อดำเนินการทดสอบครั้งต่อไป

ฉันกำลังใช้Jest v21.

นี่คือลักษณะของการทดสอบ Jest โดยทั่วไป:

__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
  });
});

นี่คือสิ่งที่ฉันได้ลองทำแล้ว:


1 - mockFn.mockImplementationOnce (fn)

ข้อดี

  • เปลี่ยนกลับสู่การใช้งานดั้งเดิมหลังจากการโทรครั้งแรก

ข้อเสีย

  • จะหยุดทำงานหากการทดสอบเรียกbใช้หลายครั้ง
  • จะไม่เปลี่ยนกลับเป็นการใช้งานดั้งเดิมจนกว่าbจะไม่มีการเรียกใช้ (รั่วไหลในการทดสอบครั้งต่อไป)

รหัส:

it('should override myModule.b mock result (and leave the other methods untouched)', () => {

  myMockedModule.b.mockImplementationOnce(() => 'overridden');

  myModule.a(); // === true
  myModule.b(); // === 'overridden'
});

2 - jest.doMock (ชื่อโมดูลโรงงานตัวเลือก)

ข้อดี

  • ล้อเลียนอีกครั้งอย่างชัดเจนในทุกการทดสอบ

ข้อเสีย

  • ไม่สามารถกำหนดการใช้งานจำลองเริ่มต้นสำหรับการทดสอบทั้งหมด
  • ไม่สามารถขยายการใช้งานเริ่มต้นเพื่อบังคับให้ประกาศวิธีการจำลองแต่ละวิธีอีกครั้ง

รหัส:

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'
});

3 - การเยาะเย้ยด้วยตนเองด้วยวิธีการตั้งค่า (ตามที่อธิบายไว้ที่นี่ )

ข้อดี

  • ควบคุมผลลัพธ์ที่เยาะเย้ยได้อย่างสมบูรณ์

ข้อเสีย

  • รหัสสำเร็จรูปจำนวนมาก
  • ยากที่จะรักษาในระยะยาว

รหัส:

__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();
});

4 - jest.spyOn (object, methodName)

ข้อเสีย

  • ฉันไม่สามารถเปลี่ยนกลับmockImplementationไปเป็นค่าส่งคืนเดิมที่จำลองไว้ได้ดังนั้นจึงส่งผลต่อการทดสอบครั้งต่อไป

รหัส:

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?
});

ดี. แต่คุณจะทำตัวเลือกที่ 2 สำหรับโมดูล npm เช่น '@ private-repo / module' ได้อย่างไร? ตัวอย่างส่วนใหญ่ที่ฉันเห็นมีเส้นทางสัมพัทธ์? สิ่งนี้ใช้ได้กับโมดูลที่ติดตั้งด้วยหรือไม่?
mrbinky3000

คำตอบ:


54

รูปแบบที่ดีสำหรับการทดสอบการเขียนคือการสร้างฟังก์ชันการตั้งค่าโรงงานที่ส่งคืนข้อมูลที่คุณต้องการสำหรับการทดสอบโมดูลปัจจุบัน

ด้านล่างนี้คือโค้ดตัวอย่างบางส่วนตามตัวอย่างที่สองของคุณแม้ว่าจะอนุญาตให้มีการกำหนดค่าเริ่มต้นและแทนที่ค่าด้วยวิธีที่ใช้ซ้ำได้

const spyReturns = returnValue => jest.fn(() => returnValue);

describe("scenario", () => {
  const setup = (mockOverrides) => {
    const mockedFunctions =  {
      a: spyReturns(true),
      b: spyReturns(true),
      ...mockOverrides
    }
    return {
      mockedModule: jest.doMock('../myModule', () => mockedFunctions)
    }
  }

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

42

วานิลลา JS

ใช้mockFn.mockImplementation (Fn)

import { funcToMock } from './somewhere';
jest.mock('./somewhere');

beforeEach(() => {
  funcToMock.mockImplementation(() => { /* default implementation */ });
});

test('case that needs a different implementation of funcToMock', () => {
  funcToMock.mockImplementation(() => { /* implementation specific to this test */ });
  // ...
});

TypeScript

นอกเหนือจากโซลูชัน Vanilla JS:

เพื่อป้องกันไม่ให้ข้อความmockImplementation ไม่ใช่คุณสมบัติของ funcToMockคุณจะต้องระบุประเภทเช่นโดยการเปลี่ยนบรรทัดบนสุดจากด้านบนเป็นดังต่อไปนี้:

import { (funcToMock as jest.Mock) } from './somewhere';

สามารถพบคำถามเกี่ยวกับปัญหานี้ได้ที่นี่: jest typescript property mock ไม่มีอยู่ในประเภท


24

สายไปหน่อย แต่ถ้ามีคนอื่นกำลังมีปัญหากับเรื่องนี้

เราใช้ TypeScript, ES6 และ babel สำหรับการพัฒนาแบบตอบสนอง

โดยปกติเราจะจำลองโมดูล 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();
    });

Gist: https://gist.github.com/thomashagstrom/e5bffe6c3e3acec592201b6892226af2

บทช่วยสอน: https://medium.com/p/b4ac52a005d#19c5

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.