Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I avoid introducing global leaks when using Squire.js with RequireJS and Mocha?

I am writing a single page JavaScript application using Backbone and Backbone.Marionette. I am using AMD modules and RequireJS to help organize my code and manage dependencies. I am also using Mocha as my testing framework for TDD/BDD.

Everything was working fine until I wanted to introduce stubs, mocks, and spies using Sinon.JS. After a lot of searching, I came across a page on test frameworks in the RequireJS wiki and Squire.js, which seemed like it would suit my needs well. However, when I try to use Squire.js to load a module, Mocha suddenly reports global leaks for the module's dependencies. There are no leaks reported if I load the module directly using Require.JS.

For example, the following test code does not cause Mocha to report any leaks:

define(['app/app'], function(app) {
    describe('App', function() {
        it('Should define a \'header\' region', function() {
            expect(app.headerRegion).to.exist;
        });

        it('Should define a \'main\' region', function() {
            expect(app.mainRegion).to.exist;
        });
    });

    return {
        name: "App"
    };
});

However, converting the code to use Squire.js as follows causes Mocha to report leaks for jQuery, Backbone, and Marionette (dependencies of app.js):

define(['Squire'], function(Squire) {
    describe('App', function() {

        var testContext = {};

        beforeEach(function(done) {
            testContext.injector = new Squire();
            testContext.injector.require(['app/app'], function(app) {
                testContext.app = app;
                done();
            });
        });

        it('Should define a \'header\' region', function() {
            expect(testContext.app.headerRegion).to.exist;
        });

        it('Should define a \'main\' region', function() {
            expect(testContext.app.mainRegion).to.exist;
        });
    });

    return {
        name: "App"
    };
});

What am I doing wrong? I am totally baffled that Mocha does not report a leak with RequireJS but does with Squire.js. I also tried some of the other solutions I found in another StackOverflow question on mocking RequireJS dependencies, such as the custom function and testr.js, prior to Squire.js and had similar results. To date, I've been unable to find an example that uses Mocha, RequireJS, and Sinon.JS all together.

I've placed my current code base on GitHub in case there is some critical information that I have left out or something. The test in question can be found in test\spec\test.app.js.

Any assistance is greatly appreciated. I'd very much like to get past monkeying with my test setup and on to actually working on my app. Thanks in advance.

like image 894
Trevor A. Avatar asked Jan 10 '13 05:01

Trevor A.


1 Answers

After thinking this through further, I realized that this is actually expected behavior and a side-effect of the timing of when app.js is loaded for testing.

My tests are loaded via RequireJS in a require statement shown below

require([
  'spec/test.smoketest',
  'spec/test.app'
  ], runMocha);

where runMocha is simply a function that simply calls mocha.run().

It occurred to me that the way Mocha most likely detects global leaks is to compare what is registered globally before and after each test is run. In the first example above, where leaks are not reported, jQuery, Backbone, and Marionette are loaded by RequireJS before mocha.run() is called as part of loading the test.app.js module. On the other hand, jQuery, Backbone, and Marionette are loaded as part of the tests themselves in the second example.

Thus, the first configuration does not report any leaks because jQuery, Backbone, and Marionette are already registered globally before mocha.run() is called. The second configuration reports leaks because they are registered during the tests.

Now that I understand what's going on and that this is expected, I'm comfortable configuring Mocha to allow these global objects. This can be done in the Mocha configuration as shown below:

mocha.setup({
    ui: "bdd",
    globals:["_", "$", "jQuery", "Backbone", "Marionette"]
});
like image 143
Trevor A. Avatar answered Sep 28 '22 03:09

Trevor A.