如何在angular 2中测试私有函数?
class FooBar {
private _status: number;
constructor( private foo : Bar ) {
this.initFooBar();
}
private initFooBar(){
this.foo.bar( "data" );
this._status = this.fooo.foo();
}
public get status(){
return this._status;
}
}
我找到了解决办法
将测试代码本身放在闭包中,或者在闭包中添加代码,以存储外部作用域中现有对象上局部变量的引用。
稍后使用工具提取测试代码。
http://philipwalton.com/articles/how-to-unit-test-private-functions-in-javascript/
如果你做过这个问题,请给我一个更好的解决方法。
P.S
大多数类似类型的问题的答案都没有给出问题的解决方案,这就是我问这个问题的原因
大多数开发人员说不要测试私有函数,但我不会说它们是错的还是对的,但我的案例中有必要测试私有函数。
不要为私有方法编写测试。这就破坏了单元测试的意义。
您应该测试类的公共API
你不应该测试类的实现细节
例子
class SomeClass {
public addNumber(a: number, b: number) {
return a + b;
}
}
如果之后实现发生变化,但公共API的行为保持不变,则该方法的测试不需要更改。
class SomeClass {
public addNumber(a: number, b: number) {
return this.add(a, b);
}
private add(a: number, b: number) {
return a + b;
}
}
不要仅仅为了测试而将方法和属性设为公共。这通常意味着:
您正在尝试测试实现,而不是API(公共接口)。
您应该将所讨论的逻辑移到它自己的类中,以使测试更容易。
我同意@toskv的观点:我不建议这么做:-)
但是如果你真的想测试你的私有方法,你可以意识到TypeScript的相应代码对应于构造函数原型的一个方法。这意味着它可以在运行时使用(然而您可能会有一些编译错误)。
例如:
export class FooBar {
private _status: number;
constructor( private foo : Bar ) {
this.initFooBar({});
}
private initFooBar(data){
this.foo.bar( data );
this._status = this.foo.foo();
}
}
将转化为:
(function(System) {(function(__moduleName){System.register([], function(exports_1, context_1) {
"use strict";
var __moduleName = context_1 && context_1.id;
var FooBar;
return {
setters:[],
execute: function() {
FooBar = (function () {
function FooBar(foo) {
this.foo = foo;
this.initFooBar({});
}
FooBar.prototype.initFooBar = function (data) {
this.foo.bar(data);
this._status = this.foo.foo();
};
return FooBar;
}());
exports_1("FooBar", FooBar);
}
}
})(System);
查看这个plunkr: https://plnkr.co/edit/calJCF?p=preview。
不要为私有方法编写测试。这就破坏了单元测试的意义。
您应该测试类的公共API
你不应该测试类的实现细节
例子
class SomeClass {
public addNumber(a: number, b: number) {
return a + b;
}
}
如果之后实现发生变化,但公共API的行为保持不变,则该方法的测试不需要更改。
class SomeClass {
public addNumber(a: number, b: number) {
return this.add(a, b);
}
private add(a: number, b: number) {
return a + b;
}
}
不要仅仅为了测试而将方法和属性设为公共。这通常意味着:
您正在尝试测试实现,而不是API(公共接口)。
您应该将所讨论的逻辑移到它自己的类中,以使测试更容易。
你可以调用私有方法!
如果您遇到以下错误:
expect(new FooBar(/*...*/).initFooBar()).toEqual(/*...*/)
// TS2341: Property 'initFooBar' is private and only accessible within class 'FooBar'
只需使用// @ts-ignore:
// @ts-ignore
expect(new FooBar(/*...*/).initFooBar()).toEqual(/*...*/)
感谢@Moff452的评论。你也可以这样写:
expect(new FooBar(/*...*/)['initFooBar']()).toEqual(/*...*/)
更新:
@ts-expect-error是@ts-ignore的更好选择。看到的:
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-9.html#ts-ignore-or-ts-expect-error
“不要测试私有方法”的重点实际上是像使用它的人那样测试类。
如果你有一个带有5个方法的公共API,你的类的任何消费者都可以使用这些方法,因此你应该测试它们。使用者不应该访问类的私有方法/属性,这意味着在公共公开功能保持不变的情况下,可以更改私有成员。
如果依赖内部可扩展功能,请使用protected而不是private。
注意protected仍然是一个公共API(!),只是使用方式不同。
class OverlyComplicatedCalculator {
public add(...numbers: number[]): number {
return this.calculate((a, b) => a + b, numbers);
}
// can't be used or tested via ".calculate()", but it is still part of your public API!
protected calculate(operation, operands) {
let result = operands[0];
for (let i = 1; i < operands.length; operands++) {
result = operation(result, operands[i]);
}
return result;
}
}
单元测试保护的属性与消费者使用它们的方式相同,通过子类化:
it('should be extensible via calculate()', () => {
class TestCalculator extends OverlyComplicatedCalculator {
public testWithArrays(array: any[]): any[] {
const concat = (a, b) => [].concat(a, b);
// tests the protected method
return this.calculate(concat, array);
}
}
let testCalc = new TestCalculator();
let result = testCalc.testWithArrays([1, 'two', 3]);
expect(result).toEqual([1, 'two', 3]);
});