代码之家  ›  专栏  ›  技术社区  ›  Nxt3

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

  •  1
  • Nxt3  · 技术社区  · 7 年前

    我有以下课程:

    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() 这会导致 伐木厂 在我不期望的时候创建。

    为什么叫这个?我试过了 fdescribe 在测试中,确保这是唯一正在运行的东西,即使这样,也无法修复它。

    1 回复  |  直到 7 年前
        1
  •  1
  •   Estus Flask    7 年前

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

    如果decorator函数产生副作用或涉及其他单元(这可能会影响单元测试),则应该对其进行重构。

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

    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);
      };
    });