测试具有完整代码覆盖范围的简单记录器功能 [英] Test simple logger functions with full code coverage

查看:48
本文介绍了测试具有完整代码覆盖范围的简单记录器功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Chai,Sinn和Instanbul来测试NodeJS应用程序.这是 Logger 代码:

I'm using Chai, Sinon and Instanbul to test a NodeJS application. Here's the Logger code:

import Debug, { IDebugger } from 'debug';

export default class Logger {
  private readonly dbg: IDebugger;

  constructor(name: string) {
    this.dbg = Debug(name);
  }

  public log(str: string): void {
    this.dbg(str);
  }
}

这是我开始做的测试:

import * as fs from 'fs';
import sinon from 'sinon';
import { expect } from 'chai';
import Logger from '../../src/utils/Logger';
import Debug from 'debug';

describe('Unit tests for Logger class', () => {
  afterEach(() => {
    sinon.restore();
  });

  describe('#constructor', () => {
    it('should set logger', () => {
      const setLoggerStub = sinon.stub(Logger.prototype, 'log');
      const logExample: string = 'test logger'
      const logger = new Logger(logExample);
      logger.log('test logger')

      sinon.assert.calledWithExactly(setLoggerStub, logExample);
    });
  });
});

代码覆盖率报告如下:

The code coverage report is the following:

我不确定为什么未测试 log 函数,如何对其进行测试,并将构造函数的测试与 log 函数的测试分开?

I'm not sure why the log function is not tested, how can I test it and separate the test for the constructor and the test for the log function?

我不确定要测试什么,因为 log 的唯一功能是将字符串传递给 debug 库.在这种情况下应该采取什么方法?

I'm not sure about what to test because the only function of log is to pass a string to the debug library. What should be the approach in this scenario?

推荐答案

log 不包括在内,因为它从未被调用,因此您将其与sinon.stub(Logger.prototype,'log').

log isn't covered because it never got called, you stubbed it out with sinon.stub(Logger.prototype, 'log').

您当前的实现方式很难测试,因为 Logger Debug 太相关了.在构造函数中创建依赖项实例通常不是一个好主意.

Your current implementation is difficult to test because Logger is too coupled to Debug. Creating instances of dependencies in the constructor is generally not a good idea.

如果反转依赖关系,则使 Logger 的构造函数使用 IDebugger 而不是 string 名称:

If you invert the dependency, making Logger's constructor take an IDebugger instead of the string name:

import Debug, { IDebugger } from 'debug';

export default class Logger {

  constructor(private readonly dbg: IDebugger) {}

  public log(str: string): void {
    this.dbg(str);
  }
}

然后,当您测试 Logger 时,您可以轻松地注入一个测试加倍项:

then when you test Logger, you can easily inject a test double:

it("debugs the input", () => {
  const debug = sinon.stub();
  const logger = new Logger(debug);
  const message = "hello, world!";

  logger.log(message);

  sinon.assert.calledWithExactly(debug, message);
});

这意味着您无需监视 Logger 本身的任何部分,从而使您可以更轻松地在该类中进行重构.现在, Logger 类仅取决于抽象的 IDebugger 接口,而不取决于具体的 Debug 实现.

This means you don't need to spy on any part of Logger itself, allowing you to refactor inside that class more easily. The Logger class now only depends on the abstract IDebugger interface, not the concrete Debug implementation.

使用给定特定名称的 Debug 创建 Logger 实例可以只是一个函数或静态方法:

Creating a Logger instance with a Debug given the specific name can just be a function, or a static method:

export default class Logger {

  static withName(name: string) {
    return new Logger(Debug(name));
  }

  /* ... */
}

这篇关于测试具有完整代码覆盖范围的简单记录器功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆