1. ホーム
  2. アンギュラー

[解決済み】Angular / TypeScriptのプライベートメソッドのユニットテストをJasmineで記述する方法

2022-04-03 13:53:26

質問

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

}

私が見つけた解決策

  1. クロージャの中にテストコードそのものを入れるか、クロージャの中に、外部スコープの既存のオブジェクトのローカル変数への参照を格納するコードを追加する。

    後でツールを使ってテストコードを取り除く。 http://philipwalton.com/articles/how-to-unit-test-private-functions-in-javascript/

この問題を解決するための良い方法があれば教えてください。

P.S

  1. このような類似の質問に対する回答のほとんどは、問題に対する解決策を与えていないため、この質問をさせていただきました。

  2. ほとんどの開発者は、プライベート関数をテストしてはいけないと言いますが、私はそれが間違っているとも正しいとも言いませんが、私の場合、プライベート関数をテストする必要性があるのです。

どのように解決するのですか?

公開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にはプライベート・メンバが存在しないのです。これは純粋にTSコンパイラのために使われているのです。そのため

  1. にアサートすることができます。 any で、コンパイラがアクセス制限について警告するのを回避することができます。

    (thing as any)._name = "Unit Test";
    (thing as any)._count = 123;
    (thing as any).init("Unit Test", 123);
    
    

    この方法の問題点は、コンパイラが、あなたが any そのため、望ましい型エラーは発生しません。

    (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
    
    

    これでは、リファクタリングが難しくなるのは明らかです。

  2. 配列アクセスを使用することができます ( [] ) を使って、プライベートメンバーを取得することができます。

    thing["_name"] = "Unit Test";
    thing["_count"] = 123;
    thing["init"]("Unit Test", 123);
    
    

    見た目は変ですが、TSC は実際には型に直接アクセスしたかのように検証します。

    thing["_name"] = 123; // type error
    thing["_count"] = "Unit Test"; // type error
    thing["init"](0, "123"); // argument error
    
    

    正直なところ、なぜこれがうまくいくのかわかりません。 これはどうやら 意図的な "escape hatch" を使えば、型安全性を失うことなくプライベートメンバーにアクセスすることができます。これはまさに、ユニットテストに必要なことだと思います。

以下は 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 はコード臭であり、docsの言うとおりです。

このコメントを使用することをお勧めします。 を惜しまない . [原文のまま強調].