web-dev-qa-db-fra.com

Mocking dans les tests unitaires TypeScript

Le problème est que se moquer de TypeScript peut devenir délicat si l'objet est suffisamment complexe (enfin dans n'importe quel langage fortement typé). Vous vous moqueriez généralement de quelques trucs supplémentaires juste pour faire la compilation de code et en C # par exemple, vous pouvez utiliser AutoFixture ou similaire. D'un autre côté, Javascript est un langage dynamique et il est possible de ne se moquer que d'une partie de l'objet nécessaire au test.

Ainsi, dans le test unitaire TypeScript, je peux déclarer ma dépendance en utilisant le type any et ainsi facilement la moquer. Voyez-vous des inconvénients à une telle approche?

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

contre

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

Mon expérience avec les tests unitaires dans TypeScript montre clairement qu'il vaut la peine de garder tous les objets fantaisie tapés. Lorsque vous quittez vos simulacres avec un type de any cela devient problématique lors d'un renommage. IDE ne découvrira pas correctement quelles occurrences du paramètre user ou settings doivent être modifiées. Bien sûr, l'écriture d'un objet fantôme manuellement avec une interface complète est vraiment laborieuse .

Heureusement, il existe deux outils pour TypeScript qui permettent de créer des objets fictifs de type sécurisé: ts-mockito (inspiré par Java mockito ) et typemoq (inspiré par C # Moq ).

17
Terite

Maintenant que TypeScript 3 est sorti, un typage fort et complet peut enfin être exprimé! J'en ai profité et j'ai porté NSubstitute sur TypeScript.

Il peut être trouvé ici: https://www.npmjs.com/package/@fluffy-spoon/substitute

J'ai fait une comparaison avec les cadres les plus populaires ici: https://medium.com/@mathiaslykkegaardlorenzen/with-TypeScript-3-and-substitute-js-you-are-already-missing-out-when-mocking -ou-truquer-a3b3240c4607

Remarquez comment il peut créer des faux à partir d'interfaces et avoir un typage fort et complet en cours de route!

Comme le souligne @Terite any, le type sur les maquettes est un mauvais choix car il n'y aurait pas de relation entre la maquette et son type/implémentation réelle. La solution améliorée peut donc être de convertir un objet partiellement simulé en type de maquette:

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

Il convient également de mentionner que TypeScript ne permettra pas de convertir un objet ayant des membres supplémentaires (sur-ensemble ou type dérivé). Signifie que votre maquette partielle est en fait de type de base à UserService et peut être castée en toute sécurité. par exemple.

// 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

Comme pour les objets fonctionnels, vous pouvez utiliser des bibliothèques factices qui prennent en charge TypeScript ou des bibliothèques javascript qui ont des définitions de type. Dans les deux cas, les types existent uniquement au moment de la conception. Donc jasminesjs a une fonctionnalité Spy et vous pouvez l'utiliser de manière sécurisée comme ceci:

spyOn(SomeTypescriptClass, "SomeTypescriptClassProperty");

Le IDE et le compilateur TypeScript le géreront correctement. Le seul inconvénient est que les paramètres ne sont pas pris en charge. Si vous avez besoin de la prise en charge de type pour le paramètre, vous devez utiliser les bibliothèques de simulation TypeScript. Je peux ajouter une autre bibliothèque de simulation pour TypeScript moq.ts

Quant aux objets DTO, vous pouvez utiliser cette approche:

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

Si je me souviens bien, IDataMock peut être remplacé par une interface partielle standard de TypeScript.

0
dvabuzyarov