loading...

Mocking a namespaced function constructor in Jasmine

dechamp profile image DeChamp ・2 min read

TL;DR

Go to bottom to see the code of how it's done.

Intro

So as I mentioned in one of my other post What is Declarative vs Imperative, that I'm going to make an effort to create a post for anytime that I have to research something.

I haven't messed with Jasmine in about a year, but now back on a project that is using it. I prefer Jest myself, but you gotta work in what the project uses sometimes.

I was having the biggest headache trying to mock one of my IFFE Immediately-invoked function expression, which has a namespace.

I chose to follow the patterns practiced in the project. I like IFFE's but sometimes they are not appropriate to use. That's for another days discussion.

Anywho, I was having a heck of a time remembering how to mock this badboy, luckily my good friend Marc Medina came to the rescue with the know-how.

Ok, so here is the setup to help you understand what I am talking about.

The setup

At work, we tend to keep our code namespaced, and using IFFE's is an easy way to lock it in, to encapsulate the logic within the namespace while blocking any outside access to the code unless purposely exposed.

For this example, our namespace will be 'dummy' and our function constructor to be called 'CoolFunction' and takes a 'mountId' argument.

note: this is aimed at ecmascript 5 do to project restraints, but feel free to apply the ideas behind it to what ever version you're writing for.

;(function(window) {
    // leaving off var/let/const 
    // (depending on version of javascript you're writing for) will make it global.
    dummy = dummy || {};
    dummy.CoolFunction = CoolFunction;

    function CoolFunction(mountId) {
        function mount: ...
        function unmount: ...
    }    

})(window);

Being a decent coder, I am testing all of my code. That includes my initializer script which calls my 'CoolFunction'. Here is where the mocking comes in to play.

Here is how we would mock the code above.

The solution

var coolFunctionSpy, mountSpy, unmountSpy;

beforeEach(function() {
    // setup your method spies first
    mountSpy = jasmine.createSpy('mount');
    unmountSpy = jasmine.createSpy('unmount');    

    // now create a spy on the main function constructor and return the object of values
    coolFunctionSpy = jasmine.createSpy('coolFunction').and.returnValue({
        mount: mountSpy,
        unmount: unmountSpy
    });

    // now assign that to your object, that you are using 
    // to overwrite the main namespace. Make sure you do not add `var` 
    // as we want to overwrite the global.
    wpt = {
        CoolFunction: coolFunctionSpy
    }
}

describe("our tests", function() {
    it("allows us to test the spied methods", function () {
        // Calls `new dummy.CoolFunction('theId')` with in.
        sut.init(); 

        // passes, because it is called in sut.init()
        expect(mountSpy).toHaveBeenCalled();         

        // passes, because it is not called in sut.init()
        expect(unmountSpy).not.toHaveBeenCalled(); 
    });
});

The important thing to note here, is NOT to add var in front of the namespace assignment. You want to overwrite the global, and if you add the var to it, it will not work.

I hope this helps others out there.

Feedback

Did I misspeak? Have suggestions? Need clarification? Feel free to comment.

Thank you!

--DeChamp

Posted on by:

dechamp profile

DeChamp

@dechamp

Just a coder and a dad. I love my family and I love to code!!!! started coding at 11, so I have 25 years under my belt. Still love learning about it every day. Follow me on instagram @codeFiend

Discussion

markdown guide