【问题标题】:Typescript + Jest: How to partially mock class instanceTypescript + Jest:如何部分模拟类实例
【发布时间】:2019-10-27 21:07:13
【问题描述】:

考虑两个类AB,如下所示:

class A {
    private b: B;

    public constructor(b: B){
        this.b=b;
    }

    public doSomething(){
        this.b.myMethod();
    }
}

class B {
    public myMethod(){...}
    public someOtherMethod(){...}
}

我想在模拟B.myMethod() 的行为的同时测试类A

目前我们这样做:

const bMock: Partial<B> = {
    myMethod: jest.fn(<some mock here>),
}

const sut = new A(bMock as any);

sut.doSomething();

expect(bMock.myMethod).toBeCalled();

我们想要实现的是类似的结果,但不必通过 as any 模拟,也不必自己模拟所有方法。检查模拟类型对我们来说非常重要,否则我们将无法通过此测试捕获模拟依赖项中的重大更改。

我们也已经查看了sinon,但在某些情况下,我们不希望调用模拟依赖项的构造函数,因此在创建对象后存根不是一种选择。对整个类进行存根会导致与上述类似的问题。

【问题讨论】:

标签: typescript jestjs


【解决方案1】:

编辑:现在我们使用jest-mock-extended 来获得更好的笑话兼容性和更多功能

我使用Substitute找到了一个不错的解决方案。

唯一的问题是缺少对自述文件中提到的 null/undefined 的检查。不过还是比使用as any 好。

import Substitute, { SubstituteOf } from '@fluffy-spoon/substitute';

class A {
  private b: B;

  public constructor(b: B) {
    this.b = b;
  }

  public doSomething() {
    return this.b.myMethod();
  }
}

class B {
  public myMethod() {
    return 'realMethod';
  }
  public someOtherMethod() {
    return 'realSomeOtherMethod';
  }
}

let bMock: SubstituteOf<B>;
beforeEach(() => {
  bMock = Substitute.for<B>();
});

test('empty mock', () => {
  const sut = new A(bMock);
  console.log(sut.doSomething()); // Output: '[Function]'
});

test('mock myMethod', () => {
  bMock.myMethod().returns('You got mocked!');
  const sut = new A(bMock);
  console.log(sut.doSomething()); // Output:  'You got mocked!'
});

test('does not compile', () => {
  bMock.myMethod().returns(1337); // Will show compilation error due to incompatible type (string vs. number)
  const sut = new A(bMock);
  console.log(sut.doSomething());
});

test('missing null checks', () => {
  bMock.myMethod().returns(); // Will not complain
  const sut = new A(bMock);
  console.log(sut.doSomething()); // Output 'undefined'
});

【讨论】:

    【解决方案2】:

    创建一个泛型怎么样? (您可以像在您的示例中一样使用 Partial,但您必须在调用它之前检查您是否提供了“sut.doSomething”实现。

    class B {
      public myMethod(){}
      public someOtherMethod(){}
    }
    
    class A<T extends B> {
      private b: T;
    
      public constructor(b: T){
          this.b=b;
      }
    
      public doSomething(){
          this.b.myMethod();
      }
    }
    
    const bMock = {myMethod: jest.fn(), someOtherMethod: jest.fn()}
    const sut = new A(bMock)
    sut.doSomething()
    expect(bMock.myMethod).toBeCalled();
    

    【讨论】:

    • 感谢您的回答。不幸的是,这对我没有帮助。使用通用 &lt;T extends B&gt; 仍然需要 T 遵循 B 的整个界面(您的 bMock 实际执行的操作)。我希望能够通过bMock 而不必担心someOtherMethod() 并且不会失去类型安全性。所以基本上是一种用适合其类型的模拟替换所有(公共)成员(包括构造函数)的方法,并且只为特定的单个方法添加模拟实现。
    • 我明白了。您可以做的最好的事情是您可以将“A”替换为“Avoid}>”并列出 A 类在那里使用的所有方法(或者可能提取到单独的界面),但这会重复你在问题中所做的工作 - 或者你可以在某个地方转换为“任何”。
    • stackoverflow.com/a/63074930/1402988 的第二部分仅展示了使用 Jest 在 TypeScript 中安全地模拟类中的单个方法。
    猜你喜欢
    • 2019-10-01
    • 2019-10-09
    • 2019-03-22
    • 2020-07-13
    • 1970-01-01
    • 2019-05-17
    • 2019-08-10
    • 2018-06-21
    • 1970-01-01
    相关资源
    最近更新 更多