开玩笑:如何模拟类的一个特定方法

2022-08-30 02:36:01

假设我有以下类:

export default class Person {
    constructor(first, last) {
        this.first = first;
        this.last = last;
    }
    sayMyName() {
        console.log(this.first + " " + this.last);
    }
    bla() {
        return "bla";
    }
}

假设我想创建一个模拟类,其中方法“sayMyName”将被模拟,方法“bla”将保持原样。

我写的测试是:

const Person = require("../Person");

jest.mock('../Person', () => {
    return jest.fn().mockImplementation(() => {
        return {sayMyName: () => {
            return 'Hello'
        }};
    });
});


let person = new Person();
test('MyTest', () => {
    expect(person.sayMyName()).toBe("Hello");
    expect(person.bla()).toBe("bla");
})

第一个“expect”语句通过,这意味着“sayMyName”被成功嘲笑。但是,第二个“期望”失败并出现错误:

TypeError: person.bla 不是一个函数

我知道被嘲笑的类删除了所有方法。我想知道如何模拟一个类,以便只有特定的方法才会被模拟。


答案 1

使用jest.spyOn()是嘲笑单个方法并保留其余方法的正确Jest方式。实际上,有两种略有不同的方法。

1. 仅在单个对象中修改方法

import Person from "./Person";

test('Modify only instance', () => {
    let person = new Person('Lorem', 'Ipsum');
    let spy = jest.spyOn(person, 'sayMyName').mockImplementation(() => 'Hello');

    expect(person.sayMyName()).toBe("Hello");
    expect(person.bla()).toBe("bla");

    // unnecessary in this case, putting it here just to illustrate how to "unmock" a method
    spy.mockRestore();
});

2. 修改类本身,以便所有实例都受到影响

import Person from "./Person";

beforeAll(() => {
    jest.spyOn(Person.prototype, 'sayMyName').mockImplementation(() => 'Hello');
});

afterAll(() => {
    jest.restoreAllMocks();
});

test('Modify class', () => {
    let person = new Person('Lorem', 'Ipsum');
    expect(person.sayMyName()).toBe("Hello");
    expect(person.bla()).toBe("bla");
});

为了完整起见,这是你如何模拟静态方法的:

jest.spyOn(Person, 'myStaticMethod').mockImplementation(() => 'blah');

答案 2

编辑 05/03/2021

我看到很多人不同意下面的方法,这很酷。不过,我确实对@blade的方法有轻微的不同意见,因为它实际上并没有测试该类,因为它使用的是.如果类发生更改,测试仍将始终通过,给出误报。下面是一个 .mockImplementationspyOn

// person.js
export default class Person {
  constructor(first, last) {
      this.first = first;
      this.last = last;
  }
  sayMyName() {
      return this.first + " " + this.last; // Adjusted to return a value
  }
  bla() {
      return "bla";
  }
}

和测试:

import Person from './'

describe('Person class', () => {
  const person = new Person('Guy', 'Smiley')

  // Spying on the actual methods of the Person class
  jest.spyOn(person, 'sayMyName')
  jest.spyOn(person, 'bla')
  
  it('should return out the first and last name', () => {  
    expect(person.sayMyName()).toEqual('Guy Smiley') // deterministic 
    expect(person.sayMyName).toHaveBeenCalledTimes(1)
  });
  it('should return bla when blah is called', () => {
    expect(person.bla()).toEqual('bla')
    expect(person.bla).toHaveBeenCalledTimes(1)
  })
});

干杯!