导出的函数在单元测试中运行,尽管从未调用它

时间:2017-11-15 14:57:47

标签: angular unit-testing jasmine

我有以下课程:

Module Module1
    Sub Main()

        With FunctionThatCreatesAnA()
            Console.WriteLine("{0}, {1}", .SomeString, .SomeInteger)
        End With

        Console.ReadLine()
    End Sub

    Function FunctionThatCreatesAnA() As ClassA
        Return New ClassA With {.SomeString = "Blah Blah", .SomeInteger = 42}
    End Function
End Module

Public Class ClassA
    Public Property SomeString() As String
    Public Property SomeInteger() As Integer
End Class

我还有以下单元测试:

import { Observable } from 'rxjs/Observable';
import * as Logger from 'js-logger';

import { CookieParser } from './cookie-parser.service';
import { LogLevelConverter } from './loglevel-converter.service';
export class LoggerFactory {
  //Logging levels can be referenced like so: `LoggerFactory.WARN`
  public static readonly DEBUG = Logger.DEBUG;
  public static readonly INFO = Logger.INFO;
  public static readonly WARN = Logger.WARN;
  public static readonly ERROR = Logger.ERROR;
  public static readonly OFF = Logger.OFF;

  private static readonly LOG_LEVEL: string = 'FACTORY_LOG_LEVEL';
  private static initialized = false;

  public static getLogger(name: string): any {
    if (!LoggerFactory.initialized) {
      LoggerFactory.init(name);
    }

    return Logger.get(name);
  }

  private static init(name: string): void {
    //Set default logging level for LoggerFactory
    const DEFAULT_LOG_LEVEL = LoggerFactory.ERROR;
    Logger.setLevel(DEFAULT_LOG_LEVEL);

    let logLevel: string;

    if (window.document.cookie.indexOf(LoggerFactory.LOG_LEVEL) > -1) {
      logLevel = CookieParser.getCookieValue(LoggerFactory.LOG_LEVEL);
    } else if (LoggerFactory.isLocalStorageSupported()) {
      logLevel = localStorage.getItem(LoggerFactory.LOG_LEVEL);
    }

    if (logLevel) {
      Logger.get(name).setLevel(LogLevelConverter.toLogLevel(logLevel));
    } else {
      Logger.get(name).setLevel(DEFAULT_LOG_LEVEL);
    }

    LoggerFactory.initialized = true;
  }

  private static isLocalStorageSupported(): boolean {
    const testKey = 'test',
      storage = window.localStorage;
    try {
      storage.setItem(testKey, '1');
      storage.removeItem(testKey);
      return true;
    } catch (error) {
      return false;
    }
  }
}

/**
 * Used for logging the lifecycles of a component
 * Usage: @NgLifecycleLog()
 * @param  {string}         name for the logger
 * @return {ClassDecorator}
 */
export function NgLifecycleLog(name?: string): ClassDecorator {
  return function(constructor: any): void {
    const LIFECYCLE_HOOKS: Array<string> = ['ngOnInit', 'ngOnChanges', 'ngOnDestroy'];

    //If no name is given, default to using the component's constructor's name
    const NAME: string = name ? name : constructor.name;

    const lifecycleLogger = LoggerFactory.getLogger(NAME);

    console.error("SHOULDN'T BE CALLED"); //gets called

    LIFECYCLE_HOOKS.forEach(hook => {
      const original = constructor.prototype[hook];

      constructor.prototype[hook] = function(...args) {
        lifecycleLogger.info(`${hook}`, ...args);
        original.apply(this, args);
      };
    });
  };
}

我的问题是即使我从不调用import { LoggerFactory } from './logger-factory.service'; describe('LoggerFactory', () => { localStorage.setItem('FACTORY_LOG_LEVEL', 'WARN'); const LOGGER_NAME = 'unit.testing.name'; const logger: any = LoggerFactory.getLogger(LOGGER_NAME); it('should provide a logger', () => { expect(logger).toBeDefined(logger); }); it('should have the correct name', () => { expect(logger.context.name).toEqual(LOGGER_NAME); }); it('should allow for localStorage log level overrides', () => { expect(logger.getLevel().name).toEqual('WARN'); }); }); 方法,它也会被调用。这是一个问题,因为我尝试在其中使用NgLifecycleLog()而不是LoggerFactory语句。当我运行单元测试时,调用console会导致在我不期望它时创建NgLifecycleLog()的实例。

为什么叫这个?我已尝试在测试中执行LoggerFactory以确保这是唯一正在运行的内容,即便如此,也无法修复它。

1 个答案:

答案 0 :(得分:1)

类装饰器在定义装饰类时执行一次。如果装饰类包含在测试包中,则将调用装饰器函数。

如果装饰器功能产生副作用或涉及其他单位(这可能影响单元测试),则应重构。

在这种情况下,装饰器修补类原型方法:

const lifecycleLogger = LoggerFactory.getLogger(NAME);

LIFECYCLE_HOOKS.forEach(hook => {
  const original = constructor.prototype[hook];

  constructor.prototype[hook] = function(...args) {
    lifecycleLogger.info(`${hook}`, ...args);
    original.apply(this, args);
  };
});

lifecycleLogger应该从在类装饰中调用的地方移动到实际使用它的地方:

LIFECYCLE_HOOKS.forEach(hook => {
  const original = constructor.prototype[hook];

  constructor.prototype[hook] = function(...args) {
    const lifecycleLogger = LoggerFactory.getLogger(NAME);
    lifecycleLogger.info(`${hook}`, ...args);
    original.apply(this, args);
  };
});