【问题标题】:initializing a class without running the constructor to test other methods with jest在不运行构造函数的情况下初始化一个类以开玩笑地测试其他方法
【发布时间】:2021-02-18 04:59:06
【问题描述】:

我有一个类,当初始化一个新实例时,它会调用一个私有验证方法,但是我想单独测试验证方法。下面是一个简单的人为示例......可能有一种方法可以重构代码或 api,以避免这种情况,但解决它以更好地理解笑话成为了挑战。这可能吗?

class Foo {
  constructor() {
    this.bar()
  }

  bar() {
    return 'bar'
  }
}

describe('bar', () => {
  let foo
  let bar

  beforeEach(() => {
    // somehow mock the constructor here
    Foo.prototype.bar = jest.fn()
    foo = new Foo()
  })

  it('should be called once', () => {
    foo.bar()
    expect(foo.bar).toBeCalledTimes(1)
  })
})

【问题讨论】:

  • bar() 不是静态的,所以如果不先创建实例就无法调用它。你不能“初始化”一个类。
  • @ChrisG - 您可以使用任意原型创建对象,包括使用Foo.prototype。 :-)
  • 作为一个在我职业生涯早期写过很多这样的测试的人,我想你会后悔积极嘲笑这样的事情。最好创建实例并检查 bar 应该做的任何事情是否已完成,而不是验证 bar 是否被调用。

标签: javascript ecmascript-6 jestjs


【解决方案1】:

您可以尝试使用jest.spyOn 方法来窥探对象方法,如下所示:

describe('bar', () => {
  let foo
  let bar

  beforeEach(() => {
    const foo = Object.create(Foo.prototype); // TJ Crowder suggestion
    // Spy object foo, listen to calls to the method bar and mock return
    jest.spyOn(foo, 'bar').mockImplementation(() => /* Custom return */);
    foo.bar();
  })

  it('should be called once', () => {
    foo.bar()
    expect(foo.bar).toBeCalledTimes(1)
  })
})

【讨论】:

    【解决方案2】:

    您可以使用相同的原型创建对象,而无需像这样运行构造函数:

    const obj = Object.create(Foo.prototype);
    

    Object.create 使用您给它的原型创建一个对象。 Foo.prototype 是分配给通过new Foo 创建的对象的原型,所以它做同样的事情,但不运行构造函数代码。 obj 将拥有所有 Foo 原型方法(bar 等),obj instanceof Foo 将为 true,但它不会运行构造函数。

    例子:

    class Foo {
        constructor() {
            console.log("Constructor ran"); // <== We don't see this
            this.bar();
        }
    
        bar() {
            return 'bar';
        }
    }
    
    const obj = Object.create(Foo.prototype);
    console.log(obj.bar()); // "bar"
    console.log(obj instanceof Foo); // true

    当然,如果Foo 构造函数代码为Foo 实例的正确性做了一些必要的事情,那么不运行该代码的一个不可避免的副作用是这些必要的事情将无法完成。但是您的示例没有构造函数这样做。


    另一种选择是将Foo 拆分为BaseFooFoo extends BaseFoo,其中BaseFoo 拥有除您在测试时不想调用的构造函数代码部分之外的所有内容。然后你会使用new BaseFoo 而不是new Foo

    【讨论】:

      【解决方案3】:

      可以模拟私有方法以用于测试目的,这可以被视为一种反射 - 除非这些是当前无法反射的 # 本地私有成员。

      在 JavaScript 中,构造函数本身就是一个类,因此模拟构造函数没有多大意义。可以使用Object.create 测试依赖原型链的继承类的方法,如其他答案所示。可以通过使用特定上下文调用它们来测试没有继承的类或对象的方法:

      // test the class
      jest.spyOn(Foo.prototype, 'bar').mockReturnValue(...);
      let foo = new Foo();
      ...
      Foo.prototype.bar.mockRestore();
      
      // test the method separately
      let fooCtx = {};    
      expect(Foo.prototype.bar.call(fooCtx, ...)).toEqual(...);
      expect(fooCtx).toEqual({...});
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-28
        • 2021-06-26
        • 1970-01-01
        • 1970-01-01
        • 2022-01-12
        • 2020-05-26
        相关资源
        最近更新 更多