如何在 Typescript 中使用 Sinon?
Posted
技术标签:
【中文标题】如何在 Typescript 中使用 Sinon?【英文标题】:How do I use Sinon with Typescript? 【发布时间】:2015-03-25 05:11:39 【问题描述】:如果我将 sinon 与 typescript 一起使用,那么如何将 sinon 模拟转换为我的对象实例?
例如,将返回一个 SinonMock,但我的被测控制器可能需要将特定服务传递给它的构造函数。
var myServiceMock: MyStuff.MyService = <MyStuff.MyService (sinon.mock(MyStuff.MyService));
controllerUnderTest = new MyStuff.MyController(myServiceMock, $log);
sinon 可以和 Typescript 一起使用吗?
【问题讨论】:
【参考方案1】:您可能需要使用<any>
类型断言来使类型变宽,然后再将其缩小为特定类型:
var myServiceMock: MyStuff.MyService =
<MyStuff.MyService> <any> (sinon.mock(MyStuff.MyService));
只是为了澄清 sinon 的一种行为 - 尽管您传递了 MyStuff.MyService
,但您传递给 mock
方法的任何内容都仅用于提供更好的错误消息。
如果你想让 mock 有方法和属性,you need to add them。
如果你想自动创建假货,你可以从tsUnit 中获取FakeFactory
,它会创建一个带有一些默认值的假货版本,你可以选择覆盖 - 在 javascript 中,这很容易(加上不使用太多的模拟功能,你可以确保你是在测试行为而不是实现)。
FakeFactory
的使用示例:
var target = tsUnit.FakeFactory.getFake<RealClass>(RealClass);
var result = target.run();
this.areIdentical(undefined, result);
【讨论】:
是的,我做了类似的事情。我还了解到我错误地使用了 sinon 对象,我没有意识到我需要做的就是在调用中使用原始的 MyService 实例,它使用了 sinon 模拟/存根。无论如何,您在这里提出的内容似乎对我的要求是正确的,谢谢! 我又回到了这个,似乎它不起作用。 myServiceMock 似乎没有任何方法。有什么想法吗? 你给它期待了吗?myServiceMock.expects("doSomething").returns(42);
我试过了,但问题似乎是 sinon 只能模拟和对象的实例,而不是基于构造函数的类型。我想这真的很有意义,因为模拟 javascript 类型会很困难。但是,这让我仍然必须构造这个类的一个实例(将模拟传递给 ctor ),然后模拟该实例。我想不出任何办法,只能手动创建模拟。【参考方案2】:
如果您使用createStubInstance
方法而不是mock
,Sinon 可以很容易地基于构造函数创建存根。
使用mocha、chai、sinon 和sinon-chai 的示例可能如下所示:
import * as sinon from 'sinon';
import * as chai from 'chai';
// ... imports for the classes under test
const expect = chai.expect;
const sinonChai = require("sinon-chai");
chai.use(sinonChai);
describe('MyController', () =>
it('uses MyService', () =>
let myService = sinon.createStubInstance(MyStuff.MyService),
controller = new MyStuff.MyController(myService as any, ...);
// ... perform an action on the controller
// that calls myService.aMethodWeAreInterestedIn
// verify if the method you're interested in has been called if you want to
expect(myService.aMethodWeAreInterestedIn).to.have.been.called;
);
);
我有 published an article,如果您想了解更多关于不同的测试替身以及如何将它们与 Sinon.js 一起使用的信息,您可能会发现它很有用。
希望这会有所帮助!
一月
【讨论】:
你也可以使用import * as sinonChai from 'sinon-chai';
,假设你已经安装了以下类型:npm i --save-dev @types/sinon-chai
。
这对我不起作用。我收到错误消息“类型‘SinonStubbedInstancecreateStubInstance
返回一个SinonStubbedInstance<Foo>
,但我需要传递给我的测试函数的不是SinonStubbedInstance<Foo>
,而是一个实际的Foo
。TypeError: The constructor should be a function.
。不幸的是,这篇文章也采用了这种“快乐路径”的方法,就好像具有多参数构造函数的类根本不存在一样!
@Frans - 将存根实例传递给消费者时使用 stubbedFoo as unknown as Foo
- 参见例如 github.com/serenity-js/serenity-js/blob/master/packages/core/…
谢谢@JanMolak。结果发现我的The constructor should be a function
错误与我假设的原因不同:我正在导入构造函数,但由于某种原因它以undefined
的身份通过,显然undefined
不是一个函数。【参考方案3】:
在 Typescript 中,这可以通过使用 sinon.createStubInstance 和 SinonStubbedInstance 类来实现。
例子:
let documentRepository: SinonStubbedInstance<DocumentRepository>;
documentRepository = sinon.createStubInstance(DocumentRepository);
现在您可以使用该类的所有存根方法进行完整的智能感知了。
示例排列:
documentRepository.delete.resolves(deletedCount: 1);
documentRepository.update.throws(error);
示例断言:
sinon.assert.calledOnce(documentRepository.update);
只有一个地方需要执行类型转换,那就是初始化要进行单元测试的类。
例子:
documentsController =
new DocumentsController(
userContext,
documentRepository as unknown as DocumentRepository);
希望这会有所帮助。 更多关于这个article。
【讨论】:
我收到一个 tslint 错误“找不到名称‘未知’。”。unknown
是在 TypeScript 3 中引入的。使用更新的 typescript 编译器为我解决了这个问题(在 IntelliJ 中:Settings > Languages & Frameworks > 打字稿.【参考方案4】:
使用ts-sinon。
它让你:
存根对象 存根接口【讨论】:
这是一个很好的答案!以上是关于如何在 Typescript 中使用 Sinon?的主要内容,如果未能解决你的问题,请参考以下文章
仅将 TypeScript 环境声明添加到特定文件(如 *.spec.ts)
如何在 Jasmine 单元测试中使用 Sinon 对 jQuery 动画进行假时间?