How to stub a Typescript-Interface / Type-definition?

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.

  • In the common setup you create a new sandbox and the mock object literals for your dependencies.
  • In the test you just specify the spies/stubs.

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.

Leave a Comment