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 :).
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.