ค้นหาเว็บไซต์

javascript - จะเปลี่ยนการจำลองการทำงานของ mock แบบเฉพาะในการทดสอบแต่ละตัวได้อย่างไร?


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

พูดสั้นๆ นี่คือสิ่งที่ฉันพยายามจะทำ

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

ฉันกำลังใช้ 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
  });
});

นี่คือสิ่งที่ฉันได้ลองทำไปจนถึงตอนนี้:

  1. 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 จะไม่ถูกเรียก (รั่วออกไปในการทดสอบถัดไป)
  2. 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'
    });
    

    ข้อดี

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

    ข้อเสีย

    • ไม่สามารถกำหนดการจำลองเริ่มต้นสำหรับการทดสอบทั้งหมดได้
    • ไม่สามารถขยายการใช้งานเริ่มต้น จึงบังคับให้ต้องประกาศซ้ำแต่ละเมธอดที่ถูกจำลอง
  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)
    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

บทความที่เกี่ยวข้อง