如何在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

大多数类似类型的问题的答案都没有给出问题的解决方案,这就是我问这个问题的原因 大多数开发人员说不要测试私有函数,但我不会说它们是错的还是对的,但我的案例中有必要测试私有函数。


当前回答

我同意@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进行单元测试”是一个很好的目标,但有时它看起来并不那么简单,你会觉得你在API和单元测试之间做出选择。你已经知道了,因为这正是你想要做的,所以我就不多说了。:)

在TypeScript中,我发现了一些为了单元测试而访问私有成员的方法。考虑这个类:

class MyThing {

    private _name:string;
    private _count:number;

    constructor() {
        this.init("Test", 123);
    }

    private init(name:string, count:number){
        this._name = name;
        this._count = count;
    }

    public get name(){ return this._name; }

    public get count(){ return this._count; }

}

尽管TS使用private、protected、public来限制对类成员的访问,但编译后的JS没有private成员,因为这在JS中是不存在的。它纯粹用于TS编译器。因此:

You can assert to any and escape the compiler from warning you about access restrictions: (thing as any)._name = "Unit Test"; (thing as any)._count = 123; (thing as any).init("Unit Test", 123); The problem with this approach is that the compiler simply has no idea what you are doing right of the any, so you don't get desired type errors: (thing as any)._name = 123; // wrong, but no error (thing as any)._count = "Unit Test"; // wrong, but no error (thing as any).init(0, "123"); // wrong, but no error This will obviously make refactoring more difficult. You can use array access ([]) to get at the private members: thing["_name"] = "Unit Test"; thing["_count"] = 123; thing["init"]("Unit Test", 123); While it looks funky, TSC will actually validate the types as if you accessed them directly: thing["_name"] = 123; // type error thing["_count"] = "Unit Test"; // type error thing["init"](0, "123"); // argument error To be honest I don't know why this works. This is apparently an intentional "escape hatch" to give you access to private members without losing type safety. This is exactly what I think you want for your unit-testing.

下面是TypeScript Playground中的一个工作示例。

编辑为TypeScript 2.6

另一个选项是使用// @ts-ignore(在TS 2.6中添加),它简单地抑制了下面一行的所有错误:

// @ts-ignore
thing._name = "Unit Test";

这样做的问题是,它抑制了下面一行的所有错误:

// @ts-ignore
thing._name(123).this.should.NOT.beAllowed("but it is") = window / {};

我个人认为@ts-ignore是一种代码气味,正如文档所说:

我们建议您尽量少使用这些注释。(强调原创)

我同意@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。

这招对我很管用:

而不是:

sut.myPrivateMethod();

这样的:

sut['myPrivateMethod']();

使用方括号调用私有方法。

Ts文件

class Calculate{
  private total;
  private add(a: number) {
      return a + total;
  }
}

spect。ts文件

it('should return 5 if input 3 and 2', () => {
    component['total'] = 2;
    let result = component['add'](3);
    expect(result).toEqual(5);
});

由于大多数开发人员不建议测试私有函数,为什么不测试它呢?

Eg.

YourClass.ts

export class FooBar {
  private _status: number;

  constructor( private foo : Bar ) {
    this.initFooBar({});
  }

  private initFooBar(data){
    this.foo.bar( data );
    this._status = this.foo.foo();
  }
}

TestYourClass.spec.ts

describe("Testing foo bar for status being set", function() {

...

//Variable with type any
let fooBar;

fooBar = new FooBar();

...
//Method 1
//Now this will be visible
fooBar.initFooBar();

//Method 2
//This doesn't require variable with any type
fooBar['initFooBar'](); 
...
}

感谢@Aaron, @Thierry Templier。