【问题标题】:How to verify results of spied function (callThrough)如何验证间谍功能的结果(callThrough)
【发布时间】:2019-03-04 21:12:08
【问题描述】:

我想验证/断言间谍功能的结果。我正在使用带有茉莉花的nestjs框架。我在我想要“监视”的方法上创建了一个 jasmine 间谍,即窃听 args 和响应/异常。但是,我无法访问间谍方法的返回值。

假设我有一个发射器和侦听器,并且我想断言我的侦听器在数据库操作失败时抛出异常。

听众:

  onModuleInit() {
    this.emitter.on('documentDeleted', d => this.onDocumentDeleted(d));
  }

  @CatchAndLogAnyException()
  private async onDocumentDeleted(dto: DocumentDeletedEventDTO) {
    this.logger.log(`Deleting document with id '${dto.id}'...`);

    const result = await this.ResearchHearingTestModel.deleteOne({ _id: dto.id });
    if (!result.ok) {
      throw new DataAccessException(
        `Deleting document with id '${dto.id}' failed. Model.deleteOne(id) result: ${result}`,
      );
    }
    if (result.n < 1) {
      throw new DocumentNotFoundException(`Deleting document with id '${dto.id}' failed.`);
    }

    this.logger.log(`Deleted document with id '${dto.id}.`);
  }

测试:

      const mockId = 123;
      const spyDelete = spyOn(model, 'deleteOne').and.returnValue({ ok: 1, n: 0 });
      const spyOnDeleted = spyOn(listener, 'onDocumentDeleted');
      spyOnDeleted.and.callThrough();

      await emitter.emit('documentDeleted', new DocumentDeletedEventDTO(mockId));

      expect(spyOnDeleted).toHaveBeenCalledTimes(1);
      expect(spyDelete).toHaveBeenCalledTimes(1);
      expect(spyDelete).toHaveBeenCalledWith(expect.objectContaining({ _id: mockId }));
      expect(spyOnDeleted).toThrow(DocumentNotFoundException);

所以在调试时,我可以看到 spyOnDeleted["[[Scopes]]"][0].spy.calls.mostRecent["[[Scopes]]"][0].calls[0].returnValue 是我可能正在寻找的一个承诺,但我无法访问它或验证它。

当我运行测试时,这是输出:

    expect(received).toThrow(expected)

    Expected name: "DocumentNotFoundException"

    Received function did not throw

       95 |       expect(spyDelete).toHaveBeenCalledTimes(1);
       96 |       expect(spyDelete).toHaveBeenCalledWith(expect.objectContaining({ _id: mockId }));
    >  97 |       expect(spyOnDeleted).toThrow(DocumentNotFoundException);
          |                            ^
       98 |     });
       99 |   });
      100 | });

我已经看到CallThrough injected spy 和其他几个类似的问题,但我仍然希望有可能监视 callThrough 方法并窃听它的输​​入/输出。有什么建议吗?

【问题讨论】:

    标签: node.js typescript jasmine nestjs


    【解决方案1】:

    toThrow 不能用于间谍。您可以使用间谍来模拟行为或使用callThrough 的实际行为,然后确保使用特定参数调用该方法。但是间谍不会知道它产生的结果(值或错误),所以你不能对其设定期望。

    如果您想测试onDocumentDeleted 的行为,您必须通过观察方法的效果来间接测试它。在您的情况下(使用@CatchAndLogAnyException),它似乎写入日志!?因此,您可以监视日志并期望它被调用并显示错误消息。或者,您可以通过将其公开来直接测试该方法。

    【讨论】:

    • 首先感谢您的回答。这与我迄今为止所看到的一致。但是通过它的间接影响来测试这个方法并不是我想要的。它可能会给出假阳性和假阴性。在这种情况下,更改记录器或我的装饰器可能会破坏侦听器的测试。我不喜欢公开该方法的建议,因为我认为这些方法不应该仅仅为了测试目的而公开。生产代码不应该适应测试,而是反过来,恕我直言。
    • 关于 toThrowtoReturn 没有捕获间谍方法的输出 - 我质疑 jasmine 间谍背后的设计决策。如果不监视他们所依附的方法,间谍应该做什么?此外,他们至少可以更新他们的文档以反映这一惊人的决定。我理解您的回答并感谢您的回答。不过,我仍在寻找符合我之前评论的解决方案。
    • 我看到了你的冲突,但如果间接测试它不是你的选择,那么你必须以某种方式将其公开。测试只能访问公共成员,因为您应该只测试公共接口。只要接口的行为没有改变,你就不必改变你的测试。如果您访问私有方法或属性,则不成立。当然,实际上有时测试公共接口是不可能的。但是我看到的唯一选择是将方法(或提取的类)公开或使用像service['privateMethod'](params) 这样的Javascript hack。
    • 我不确定我是否完全同意“不要更改测试代码”范式。当然,您不应该将您的生产代码与测试助手混在一起,否则就不需要了。但是有些代码比其他代码更适合单元测试。在为代码做出设计决策时考虑测试并不是一个坏主意。 TDD 的整个概念基于创建可良好测试的代码的思想;这是一种设计模式。
    • 再想一想,你关于测试私有/公共方法的观点在这里很适用。我不愿意提高这些方法的可见性,正是因为它们在侦听器中,它可以是沉默的观察者,隐藏 [它的方法] 并侦听事件以触发自身。但是,一旦我决定对其行为进行测试,我必须以某种方式使直接调用可用于测试,以便单独测试其逻辑。 [要么在其中将它们公开,要么将这些方法委托给其他接口。] 感谢您消除这种对设计模式的误解。
    猜你喜欢
    • 1970-01-01
    • 2020-03-22
    • 1970-01-01
    • 1970-01-01
    • 2013-03-31
    • 1970-01-01
    • 1970-01-01
    • 2018-12-26
    • 2020-10-26
    相关资源
    最近更新 更多