1

So I'm using Typescript/RequireJs/Jasmine, and can't get my spy to work for modules loaded in another module.

Here is my Spec:

import { CrmWebApiLib }     from "../../../webresources/allgnt_/scripts/Allgnt.RestLib";
import { TextFormatter }    from "../../../webresources/new_/scripts/script/TextFormatter";

describe("Test", () => {
    it("A Test", () => {
        spyOn(CrmWebApiLib, "create").and.callFake((a, b) => { });

        TextFormatter.test();

        expect(CrmWebApiLib.create).toHaveBeenCalled();
    });

    it("B Test", () => {
        var stub = {
            CrmWebApiLib: {
                create(a, b) { }
            }
        };
        define("../../../webresources/allgnt_/scripts/Allgnt.RestLib", [], stub as any);
        spyOn(stub.CrmWebApiLib, "create").and.callFake((a, b) => { });

        TextFormatter.test();

        expect(CrmWebApiLib.create).toHaveBeenCalled();
    });
});

Here is the TextFormatter:

import { RestLib, CrmWebApiLib }    from "../../../allgnt_/scripts/allgnt.restlib";

export module TextFormatter {
    export function test() {
        CrmWebApiLib.create("A", "Test");
    }
}

In either "A Test" or "B Test" I can't get the spy to function. It always calls the actual implementation and errors. I can change the TextFormatter to allow for it's definition of TextFormatter to be injected in, but that just feels wrong.

How do I get the spyOn call to work in situations like this?

1 Answer 1

0

Figured out the answer, use Squire.js.

it("A Test", (done) => {
    const injector = new Squire();
    var mock = { CrmWebApiLib: { create: () => { } }};

    spyOn(mock.CrmWebApiLib, "create").and.callFake((a, b) => { });
    injector
        .mock("allgnt_/scripts/allgnt.restlib", mock)
        .require(["new_/scripts/script/Dfnd.TextFormatter"], (mod) => {
            mod.TextFormatter.test();   
            expect(mock.CrmWebApiLib.create).toHaveBeenCalled();
            done();
        });

});

Only had two hiccups:

  1. Installed Squire, rather than Squirejs,
  2. It does not support relative paths.
3
  • You mean Squire. I don't recommend it for more than super simple projects. It breaks basic axioms of RequireJS. The most problematic one is that modules cease to be singletons when used with Squire. The symptoms this causes can be very hard to trace back to their root cause. It is possible to work around it but for more than super simple project it can require a lot of boilerplate to get the mocked environment in a good state. (See this issue for an example.)
    – Louis
    Commented Feb 22, 2017 at 15:13
  • @Louis, Yes, I did mean Squire, fixed. If squire is not the solution, than what is?
    – Daryl
    Commented Feb 22, 2017 at 15:36
  • There's no "one-size-fits-all" solution. In some projects I've been able to work around the problem by loading the code under test in an iframe. This allowed me to fiddle with the RequireJS configuration before launching the test. (JSDom in Node would also allow the same without a browser.) That made sense for those projects, but is not a general solution. Sometimes, I've redesigned the code to allow for easy testing. TextFormatter could be a class that has a test method. The constructor of TextFormatter could take a parameter that provides the object on which .create is called.
    – Louis
    Commented Feb 22, 2017 at 15:44

Not the answer you're looking for? Browse other questions tagged or ask your own question.