繁体   English   中英

模拟打字稿单元测试

[英]Mocking in Typescript unit tests

问题是如果对象足够复杂(在任何强类型语言中),在Typescript中进行模拟可能会变得棘手。 你通常会为了编译代码而模拟一些额外的东西,例如,在C#中你可以使用AutoFixture或类似的东西。 另一方面,Javascript是动态语言,并且可以仅模拟测试运行所需的对象的一部分。

所以在Typescript单元测试中,我可以使用any类型声明我的依赖项,因此很容易模拟它。 你看到这种方法的任何缺点吗?

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

VS

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

我在TypeScript中使用单元测试的经验肯定表明保持所有模拟对象的输入是值得的。 如果您将模拟类型设置为any类型,则在重命名时会出现问题。 IDE将无法正确发现应更改哪些usersettings参数。 当然,使用完整的界面手动编写模拟对象非常费力。

幸运的是,TypeScript有两种工具可以创建类型安全的模拟对象: ts-mockito (受Java mockito启发)和typemoq (受C#Moq启发)。

现在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类型的模拟都是不好的选择,因为mock和它的实际类型/实现之间没有任何关系。 所以改进的解决方案可能是将部分模拟对象转换为模拟类型:

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
}

对于功能对象,您可以使用支持typescript的模拟库或具有类型定义的javascript库。 在这两种情况下,类型仅存在于设计时。 所以jasminesjs有Spy功能,你可以像这样使用类型安全方式:

spyOn(SomeTypescriptClass, "SomeTypescriptClassProperty");

IDE和typescript编译器将正确处理它。 唯一的缺点是不支持参数。 如果需要类型支持参数,则需要使用typescript mock库。 我可以为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接口。

暂无
暂无

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

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