I work with Typescript on an AngularJS 1.X project. I use different Javascript libraries for different purposes. To unit-test my source I would like to stub some dependencies using the Typings (= interfaces). I don't want to use the ANY-type and neither to write an empty method for each interface method.
Im looking for a way to do something like that:
let dependency = stub(IDependency); stub(dependency.b(), () => {console.log("Hello World")}); dependency.a(); // --> Compile, do nothing, no exception dependency.b(); // --> Compile, print "Hello World", no exception
The pain I have right now, is that I either use any
and implement all methods which get called in my test case or I implement the interface and implement the full interface. That's too much useless code :(.
How can I generate an object that has an empty implementation for each method and is typed? I use Sinon for mocking purposes, but im open to use other libraries too.
PS: I know that Typescript erases the interfaces...but I still would like to solve that :).
In TypeScript, an interface is an abstract type that tells the compiler which property names a given object can have. TypeScript creates implicit interfaces when you define an object with properties. It starts by looking at the object's property name and data type using TypeScript's type inference abilities.
It's a simple function that takes a Partial as a parameter and returns that partial casted as the desired type. Here's the previous post's example, with using the stub function instead of casting the dependency to type any .
TypeScript Interface TypeTypeScript allows you to specifically type an object using an interface that can be reused by multiple objects. To create an interface, use the interface keyword followed by the interface name and the typed object.
Interfaces and Inheritance An interface can be extended by other interfaces. In other words, an interface can inherit from other interface. Typescript allows an interface to inherit from multiple interfaces. Use the extends keyword to implement inheritance among interfaces.
I have been writing Typescript tests using qUnit and Sinon, and I have experienced exactly the same pain you are describing.
Let's assume you have a dependency on an interface like:
interface IDependency { a(): void; b(): boolean; }
I have managed to avoid the need of additional tools/libraries by using a couple of approaches based on sinon stubs/spies and casting.
Use an empty object literal, then directly assign sinon stubs to the functions used in the code:
//Create empty literal as your IDependency (usually in the common "setup" method of the test file) let anotherDependencyStub = <IDependency>{}; //Set stubs for every method used in your code anotherDependencyStub.a = sandbox.stub(); //If not used, you won't need to define it here anotherDependencyStub.b = sandbox.stub().returns(true); //Specific behavior for the test //Exercise code and verify expectations dependencyStub.a(); ok(anotherDependencyStub.b()); sinon.assert.calledOnce(<SinonStub>anotherDependencyStub.b);
Use object literal with empty implementations of the methods needed by your code, then wrap methods in sinon spies/stubs as required
//Create dummy interface implementation with only the methods used in your code (usually in the common "setup" method of the test file) let dependencyStub = <IDependency>{ a: () => { }, //If not used, you won't need to define it here b: () => { return false; } }; //Set spies/stubs let bStub = sandbox.stub(dependencyStub, "b").returns(true); //Exercise code and verify expectations dependencyStub.a(); ok(dependencyStub.b()); sinon.assert.calledOnce(bStub);
They work quite nice when you combine them with sinon sandboxes and common setup/teardown like the one provided by qUnit modules.
Something like this (using the first option, but would work the same way if you were using the second option):
QUnit["module"]("fooModule", { setup: () => { sandbox = sinon.sandbox.create(); dependencyMock = <IDependency>{}; }, teardown: () => { sandbox.restore(); } }); test("My foo test", () => { dependencyMock.b = sandbox.stub().returns(true); var myCodeUnderTest = new Bar(dependencyMock); var result = myCodeUnderTest.doSomething(); equal(result, 42, "Bar.doSomething returns 42 when IDependency.b returns true"); });
I would agree this is still not the ideal solution but it works reasonably well, doesn't require extra libraries and keeps the amount of extra code needed to a low manageable level.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With