简体   繁体   English

模拟打字稿单元测试

[英]Mocking in Typescript unit tests

The problem is that mocking in Typescript can get tricky if the object is complex enough (well in any strongly-typed language). 问题是如果对象足够复杂(在任何强类型语言中),在Typescript中进行模拟可能会变得棘手。 You would usually mock some extra stuff just to make code compile and in C# for instance, you can use AutoFixture or similar. 你通常会为了编译代码而模拟一些额外的东西,例如,在C#中你可以使用AutoFixture或类似的东西。 On the other hand Javascript is dynamic language and it's possible to mock only part of the object that's needed for test to run. 另一方面,Javascript是动态语言,并且可以仅模拟测试运行所需的对象的一部分。

So in Typescript unit test I can declare my dependency using any type and thus easily mock it. 所以在Typescript单元测试中,我可以使用any类型声明我的依赖项,因此很容易模拟它。 Do you see any drawbacks of such approach? 你看到这种方法的任何缺点吗?

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

vs VS

let userServiceMock: any = {
    user: {
         setting: {
             showAvatar: true
         }
    }
}

My experience with unit tests in TypeScript definitely shows that it's worth to keep all mock object typed. 我在TypeScript中使用单元测试的经验肯定表明保持所有模拟对象的输入是值得的。 When you leave your mocks with a type of any it becomes problematic during a rename. 如果您将模拟类型设置为any类型,则在重命名时会出现问题。 IDE won't correctly discover which occurrences of the user or settings param should be changed. IDE将无法正确发现应更改哪些usersettings参数。 Of course writing mock object manually with a complete interface is really laborious. 当然,使用完整的界面手动编写模拟对象非常费力。

Fortunately there are two tools for TypeScript that allows creating type-safe mock objects: ts-mockito (inspired by Java mockito ) and typemoq (inspired by C# Moq ). 幸运的是,TypeScript有两种工具可以创建类型安全的模拟对象: ts-mockito (受Java mockito启发)和typemoq (受C#Moq启发)。

Now that TypeScript 3 is out, full strong typing can finally be expressed! 现在TypeScript 3已经完成,最终可以表达完整的强类型! I took advantage of this and ported NSubstitute to TypeScript. 我利用了这个并将NSubstitute移植到TypeScript。

It can be found here: https://www.npmjs.com/package/@fluffy-spoon/substitute 它可以在这里找到: https//www.npmjs.com/package/@fluffy-spoon/substitute

I made a comparison versus most popular frameworks here: https://medium.com/@mathiaslykkegaardlorenzen/with-typescript-3-and-substitute-js-you-are-already-missing-out-when-mocking-or-faking-a3b3240c4607 我在这里与大多数流行的框架进行了比较: https//medium.com/@mathiaslykkegaardlorenzen/with-typescript-3-and-substitute-js-you-are-already-missing-out-when-mocking-or-faking -a3b3240c4607

Notice how it can create fakes from interfaces, and have full strong typing along the way! 注意它如何从界面创建假货,并在整个过程中有完整的强类型!

As pointed out by @Terite any type on mocks is poor choice as there would be no relationship between mock and its actual type / implementation. 正如@Terite所指出的, any类型的模拟都是不好的选择,因为mock和它的实际类型/实现之间没有任何关系。 So improved solution may be casting partially-mocked object to mocks type: 所以改进的解决方案可能是将部分模拟对象转换为模拟类型:

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

It also worth mention that Typescript won't allow to cast any object that has extra members (superset or derived type). 值得一提的是,Typescript不允许转换任何具有额外成员(超集或派生类型)的对象。 Means that your partial mock is actually of base type to the UserService and can be safely cast. 意味着您的部分模拟实际上是UserService的基类型,可以安全地进行转换。 eg 例如

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

As for functional objects you can use mock libraries that support typescript, or javascript libraries that have type definitions. 对于功能对象,您可以使用支持typescript的模拟库或具有类型定义的javascript库。 In both cases types exist only in design time. 在这两种情况下,类型仅存在于设计时。 So jasminesjs has Spy functionality and you can use it in type safety way like this: 所以jasminesjs有Spy功能,你可以像这样使用类型安全方式:

spyOn(SomeTypescriptClass, "SomeTypescriptClassProperty");

The IDE and typescript compiler will handle it properly. IDE和typescript编译器将正确处理它。 The only drawback is that parameters are not supported. 唯一的缺点是不支持参数。 If you need type support for parameter you need to use typescript mock libraries. 如果需要类型支持参数,则需要使用typescript mock库。 I can add another mock library for typescript moq.ts 我可以为typescript moq.ts添加另一个模拟库

As for DTO objects you can use this approach: 对于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"});

As I remember IDataMock can be replaced with standard Partial interface from typescript. 我记得IDataMock可以替换为typescript的标准Partial接口。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM