我有以下课程:
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
以确保这是唯一正在运行的内容,即便如此,也无法修复它。
答案 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);
};
});