Javascript 笑话:如何模拟类的一个特定方法

Javascript 笑话:如何模拟类的一个特定方法,javascript,jestjs,Javascript,Jestjs,让我们假设我有以下类: export default class Person { constructor(first, last) { this.first = first; this.last = last; } sayMyName() { console.log(this.first + " " + this.last); } bla() { return "bla"; } }

让我们假设我有以下类:

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'被成功模拟。但是,第二个“expect”失败并出现错误:

TypeError:person.bla不是函数

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

不模拟该类,您可以这样扩展它:

class MockedPerson extends Person {
  sayMyName () {
    return 'Hello'
  }
}
// and then
let person = new MockedPerson();
const person = new Person();
const res = await person.sayMyName();
expect(res).toEqual('my name is dev');
2021年3月5日编辑 我看到很多人不同意下面的方法,这很酷。不过,我确实有点不同意@blade的方法,因为它实际上没有测试类,因为它使用的是
mockImplementation
。如果类发生变化,测试仍然会通过,并给出误报。下面是一个使用
spyOn
的示例

//person.js
导出默认类人员{
构造函数(第一个,最后一个){
this.first=first;
this.last=last;
}
sayMyName(){
返回this.first+“”+this.last;//调整为返回值
}
bla(){
返回“bla”;
}
}
以及测试:

从“/”导入人员
描述('Person class',()=>{
const person=新人('Guy','Smiley')
//监视Person类的实际方法
开玩笑的间谍(人称“sayMyName”)
开玩笑的间谍(人称“bla”)
它('应该返回名字和姓氏',()=>{
expect(person.sayMyName()).toEqual('Guy Smiley')//
期望(person.sayMyName)。已被催收时间(1)
});
它('调用blah时应返回bla',()=>{
expect(person.bla()).toEqual('bla'))
期望(person.bla).已被催收时间(1)
})
});

干杯 如果您使用的是Typescript,则可以执行以下操作:

Person.prototype.sayMyName = jest.fn().mockImplementationOnce(async () => 
        await 'my name is dev'
);
在你的测试中,你可以这样做:

class MockedPerson extends Person {
  sayMyName () {
    return 'Hello'
  }
}
// and then
let person = new MockedPerson();
const person = new Person();
const res = await person.sayMyName();
expect(res).toEqual('my name is dev');

希望这对别人有帮助

我将@sesamechick和@Billy Reilly answers结合起来创建了一个util函数,该函数模拟(一个或多个)类的特定方法,而不会对类本身产生任何影响

/**
* @CrazySynthax class, a tiny bit updated to be able to easily test the mock.
*/
class Person {
    constructor(first, last) {
        this.first = first;
        this.last = last;
    }

    sayMyName() {
        return this.first + " " + this.last + this.yourGodDamnRight();
    }

    yourGodDamnRight() {
        return ", you're god damn right";
    }
}

/**
 * Return a new class, with some specific methods mocked.
 *
 * We have to create a new class in order to avoid altering the prototype of the class itself, which would
 * most likely impact other tests.
 *
 * @param Klass: The class to mock
 * @param functionNames: A string or a list of functions names to mock.
 * @returns {Class} a new class.
 */
export function mockSpecificMethods(Klass, functionNames) {
    if (!Array.isArray(functionNames))
        functionNames = [functionNames];

    class MockedKlass extends Klass {
    }

    const functionNamesLenght = functionNames.length;
    for (let index = 0; index < functionNamesLenght; ++index) {
        let name = functionNames[index];
        MockedKlass.prototype[name] = jest.fn();
    };

    return MockedKlass;
}

/**
* Making sure it works
*/
describe('Specific Mocked function', () => {
    it('mocking sayMyName', () => {
        const walter = new (mockSpecificMethods(Person, 'yourGodDamnRight'))('walter', 'white');

        walter.yourGodDamnRight.mockReturnValue(", that's correct"); // yourGodDamnRight is now a classic jest mock;

        expect(walter.sayMyName()).toBe("walter white, that's correct");
        expect(walter.yourGodDamnRight.mock.calls.length).toBe(1);

        // assert that Person is not impacted.
        const saul = new Person('saul', 'goodman');
        expect(saul.sayMyName()).toBe("saul goodman, you're god damn right");
    });
});
/**
*@CrazySynthax类,稍作更新,可以轻松测试模拟。
*/
班主任{
构造函数(第一个,最后一个){
this.first=first;
this.last=last;
}
sayMyName(){
返回this.first+“”+this.last+this.yourdamnright();
}
你这该死的{
回答“你他妈的是对的”;
}
}
/**
*返回一个新类,并模拟某些特定方法。
*
*我们必须创建一个新类,以避免改变类本身的原型,这将
*最有可能影响其他测试。
*
*@param-Klass:要模拟的类
*@param functionNames:要模拟的字符串或函数名列表。
*@返回{Class}一个新类。
*/
导出函数mockSpecificMethods(Klass、functionNames){
if(!Array.isArray(函数名))
functionNames=[functionNames];
类MockedKlass扩展了Klass{
}
const functionnameslength=functionNames.length;
for(让index=0;index{
它('嘲笑说我的名字',()=>{
const walter=new(mockSpecificMethods(Person,'yourgodamnright'))('walter','white');
walter.yourGodDamnRight.mockReturnValue(“,这是正确的”);//yourGodDamnRight现在是一个经典的笑话模拟;
expect(walter.sayMyName()).toBe(“walter white,没错”);
期待(沃尔特,你的该死的权利,模仿,呼叫,长度),托比(1);
//断言该人员未受到影响。
const saul=新人(“saul”、“goodman”);
期待(saul.sayMyName()).toBe(“索尔·古德曼,你他妈的说得对”);
});
});

一直在问类似的问题,我想我找到了解决办法。无论实际在哪里使用Person类实例,这都应该有效

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

jest.mock("../Person", function () {
    const { default: mockRealPerson } = jest.requireActual('../Person');

    mockRealPerson.prototype.sayMyName = function () {
        return "Hello";
    }    

    return mockRealPerson
});

test('MyTest', () => {
    const person = new Person();
    expect(person.sayMyName()).toBe("Hello");
    expect(person.bla()).toBe("bla");
});
使用是模仿单个方法而不使用其他方法的恰当玩笑方式。实际上有两种稍微不同的方法

一,。仅在单个对象中修改该方法

从“/Person”导入人员;
测试('仅修改实例',()=>{
let person=新人(“Lorem”、“Ipsum”);
让spy=jest.spyOn(person,'sayMyName').mockImplementation(()=>'Hello');
期望(person.sayMyName()).toBe(“你好”);
期望(person.bla()).toBe(“bla”);
//在本例中不需要,将其放在这里只是为了说明如何“卸载”方法
spy.mockRestore();
});
二,。修改类本身,使所有实例都受到影响

从“/Person”导入人员;
以前(()=>{
spyOn(Person.prototype,'sayMyName').mock实现(()=>'Hello');
});
毕竟(()=>{
开玩笑。恢复记忆();
});
测试('修改类',()=>{
let person=新人(“Lorem”、“Ipsum”);
期望(person.sayMyName()).toBe(“你好”);
期望(person.bla()).toBe(“bla”);
});
为了完整性,下面是模拟静态方法的方式:

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

并没有真正回答这个问题,但我想展示一个用例,其中您希望模拟一个依赖类来验证另一个类

例如:
Foo
取决于
Bar
。内部
Foo
创建了
Bar
的实例。您想要模拟
来测试
Foo

酒吧类

class Bar {
  public runBar(): string {
    return 'Real bar';
  }
}

export default Bar;
import Bar from './Bar';

class Foo {
  private bar: Bar;

  constructor() {
    this.bar = new Bar();
  }

  public runFoo(): string {
    return 'real foo : ' + this.bar.runBar();
  }
}

export default Foo;


Foo类

class Bar {
  public runBar(): string {
    return 'Real bar';
  }
}

export default Bar;
import Bar from './Bar';

class Foo {
  private bar: Bar;

  constructor() {
    this.bar = new Bar();
  }

  public runFoo(): string {
    return 'real foo : ' + this.bar.runBar();
  }
}

export default Foo;


测试:

import Foo from './Foo';
import Bar from './Bar';

jest.mock('./Bar');

describe('Foo', () => {
  it('should return correct foo', () => {
    // As Bar is already mocked,
    // we just need to cast it to jest.Mock (for TypeScript) and mock whatever you want
    (Bar.prototype.runBar as jest.Mock).mockReturnValue('Mocked bar');
    const foo = new Foo();
    expect(foo.runFoo()).toBe('real foo : Mocked bar');
  });
});


注意:如果使用箭头函数定义类中的方法,则此操作无效


另见

我不知道这个问题的答案,所以