web-dev-qa-db-ja.com

TypeScript単体テストのモック

問題は、オブジェクトが十分に複雑な場合(厳密に型指定された言語であれば)TypeScriptでのモックが扱いにくくなる可能性があることです。通常は、コードをコンパイルするためだけにいくつか追加のモックを作成します。たとえば、C#では、AutoFixtureなどを使用できます。一方、JavaScriptは動的言語であり、テストの実行に必要なオブジェクトの一部のみを模擬することが可能です。

TypeScript単体テストでは、any型を使用して依存関係を宣言できるため、簡単にモックできます。そのようなアプローチの欠点はありますか?

let userServiceMock: MyApp.Services.UserService = {
    // lots of thing to mock
}

let userServiceMock: any = {
    user: {
         setting: {
             showAvatar: true
         }
    }
}
13
Andriy Horen

TypeScriptでの単体テストの経験から、すべてのモックオブジェクトを入力したままにしておく価値があることは明らかです。モックをanyのタイプのままにすると、名前の変更中に問題が発生します。 IDEは、userまたはsettings paramのどのオカレンスを変更する必要があるかを正しく検出しません。もちろん、完全なインターフェースを使用して手動でモックオブジェクトを作成するのは非常に面倒です。

幸い、タイプセーフなモックオブジェクトの作成を可能にするTypeScriptの2つのツールがあります。 ts-mockitoJava mockito に触発されたもの)と typemoq (触発されたもの) C#Moq )。

17
Terite

TypeScript 3がリリースされたので、完全な強力なタイピングを表現することができます!私はこれを利用して、NSubstituteをTypeScriptに移植しました。

それはここにあります: https://www.npmjs.com/package/@fluffy-spoon/substitute

私はここで最も人気のあるフレームワークと比較しました: https://medium.com/@mathiaslykkegaardlorenzen/with-TypeScript-3-and-substitute-js-you-are-already-missing-out-when-mocking -or-faking-a3b3240c4607

インターフェースから偽物を作成し、途中で完全に強力なタイピングを行う方法に注意してください!

@Teriteが指摘したように、モックと実際のタイプ/実装の間には関係がないため、モックのanyタイプは不適切な選択です。したがって、改善されたソリューションは、部分的にモックされたオブジェクトをモックタイプにキャストすることです。

export interface UserService {
    getUser: (id: number) => User;
    saveUser: (user: User) => void;
    // ... number of other methods / fields
}

…….

let userServiceMock: UserService = <UserService> {
    saveUser(user: User) { console.log("save user"); }
}
spyOn(userServiceMock, 'getUser').andReturn(new User());
expect(userServiceMock.getUser).toHaveBeenCalledWith(expectedUserId);

TypeScriptでは、追加のメンバー(スーパーセットまたは派生型)を持つオブジェクトをキャストできないことにも言及する価値があります。部分モックは実際にはUserServiceの基本型であり、安全にキャストできることを意味します。例えば.

// Error: Neither type '...' nor 'UserService' is assignable to the other.
let userServiceMock: UserService = <UserService> {
     saveUser(user: User) { console.log("save user"); },
     extraFunc: () => { } // not available in UserService
}
2
Andriy Horen

機能オブジェクトについては、TypeScriptをサポートするモックライブラリ、またはタイプ定義を持つJavaScriptライブラリを使用できます。どちらの場合も、タイプは設計時にのみ存在します。したがって、jasminesjsにはスパイ機能があり、次のようなタイプセーフな方法で使用できます。

spyOn(SomeTypescriptClass, "SomeTypescriptClassProperty");

IDEとTypeScriptコンパイラはそれを適切に処理します。唯一の欠点は、パラメータがサポートされていないことです。パラメータの型サポートが必要な場合は、TypeScriptモックライブラリを使用する必要があります。別のモックライブラリを追加できますTypeScriptの場合 moq.ts

DTOオブジェクトについては、このアプローチを使用できます。

export type IDataMock<T> = {
  [P in keyof T]?: IDataMock<T[P]>;
};

export function dataMock<T>(instance: IDataMock<T>): T {
  return instance as any;
}

// so where you need
const obj = dataMock<SomeBigType>({onlyOneProperty: "some value"});

私が覚えているように、IDataMockはTypeScriptの標準のPartialインターフェイスに置き換えることができます。

0
dvabuzyarov