web-dev-qa-db-ja.com

TypeScriptで記述されたクラスのプライベートメソッドをシノンを使用してスタブ化する方法

TypeScript(Node JS)で記述されたクラスのプライベートメソッドを呼び出すパブリックメソッドの単体テストを作成しています。

サンプルコード

class A {
   constructor() {  
   }
   public method1() {  
       if(this.method2()) {
          // Do something
       } else {
          // Do something else
       }
   }
   private method2() {
      return true;
   }
}

次に、method1()をテストするために、プライベートメソッドであるmethod2()をスタブする必要があります。

ここで私が試していること:

sinon.stub(A.prototype, "method2");

TypeScriptがエラーをスローしています:

Argument of type '"method2"' is not assignable to parameter of type '"method1"'

任意の助けいただければ幸いです。ありがとうございました

16

問題は、sinonの定義がstub関数の次の定義を使用することです。

interface SinonStubStatic { <T>(obj: T, method: keyof T): SinonStub; }

つまり、2番目のパラメーターはTタイプのメンバー(パブリックパラメーター)の名前でなければなりません。これはおそらく一般的に良い制限ですが、この場合は少し制限が多すぎます。

anyにキャストすることで回避できます:

sinon.stub(A.prototype, <any>"method2");

コードとテストの複雑さがより重要な場合、私はプライベートメソッドを「外部化」することを好むことがあります。 (部分)クラスまたは(部分)インターフェースのいずれかでそれを行うことができます。

    it('private methods test', async () => {
        // original class
        class A{
            public method1():string{
                if(this.method2()) {
                    // Do something
                    return "true";
                 } else {
                    // Do something else
                    return "false";
                 }
            }
            // with private method
            private method2():boolean{
                return true;
            }
        }

        // interface that makes the private method public
        interface IAExternalized{
            method2():boolean;
        }

        // class that makes the private method public
        class APrivate implements IAExternalized{
            // with public method
            method2():boolean{
                return true;
            };
        }

        // test before mocking
        let test:A = new A();
        let result:string = test.method1();
        result.should.be.equal("true");

        // let's mock the private method, but with typechecking available
        let stubMethod2:sinon.SinonStub = sinon.stub(<IAExternalized><unknown>(A.prototype), "method2").returns(false);

        result = test.method1();
        result.should.not.be.equal("true");
        result.should.be.equal("false");

        // access private method of an object through public-interface
        let testPrivate:IAExternalized = <IAExternalized><unknown>test;
        let result2:boolean = testPrivate.method2();
        result2.should.not.be.equal(true);
        result2.should.be.equal(false);
    });

注:テストするコードを制御する場合、コードを2重にする必要はなく、間違いが起こりやすくなりますが、クラスにインターフェイスを実装させることができます。標準(プライベートなし)インターフェースを「外部化された」インターフェースに変換するには、パブリックメソッドで拡張できます。

export interface IAExternalized extends IAPrivate {
   method2():boolean
};
0
mPrinC