jasmine mock window object

toy picture toy · Jan 19, 2012 · Viewed 50.8k times · Source

How do I mock window object? I'm doing firefox extension and I want to use jasmine for javascript testing.

In my javascript I have


function submit() {
...
var url = window.arguments[0];
...
}

Obviously, I have to mock window.arguments[0] in jasmine because that object doesn't exists if not passing any parameter from window.openDialog

This is my attempt to mock it with "with"


it("should submit to server", function() {

        var localContext = {
            "window": {
                arguments: ["http://localhost"]
            }

        }

        with(localContext);

But I still get this error TypeError: Cannot read property '0' of undefined, it's like when the test is run window.arguments[0] gets wiped out with the real window, because if I do

window.arguments[0]

inside the test, it prints out "http://localhost" correctly. but when it comes to submit() method it shows the error that window.argument is undefined.

Answer

Andreas Köberle picture Andreas Köberle · Jan 19, 2012

The problem is that you just overwrite a property of the window object. And if you can do that, the browser can do that as well. So mocking a function or property of a global object that everyone can access isn't a good idea in general, because you can never be sure that your changes will be there when you try to access them.

Which brings me to dependency injection. Its a common pattern to make your code unit testable, with a focus on unit. Whats does it mean. When ever you create a new object or access a global object, you're not only testing your unit functionality, but also the functionality of your newly created or global object. To prepend that, you not create the new objects in your unit, but pass them into. Normally you would to this in the constructor, but as in JavaScript function are objects with the function body as constructor, you can also pass the dependencies simply into your function.

So in your case, the function depends on the global window object. So instead of trying to access the global window object, pass it as a parameter into your function. Doing it this way you can pass in the window object in your production code and a simple JavaScript object with an arguments atribute into your test:

function youWannaTest(w){
    console.log(w.arguments[0]);
}

In your extension call the function like this:

youWannaTest(window);

In your test call the function like this:

youWannaTest({arguments: ['someValue']});