Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Squire.js example with Jasmine and RequireJS

Tags:

I want to write JS tests. The production code is written with RequireJS. I found a test lib named Squire.js: https://github.com/iammerrick/Squire.js/

From the Squire.js website

Run generates a function that will receive a done callback and execute it after your test function is complete. Particularly useful for frameworks where asynchrony is handled with a callback. Here is an example with Mocha.js. Jasmine can offer this callback approach using Jasmin.Async."

I don't know how to use this with Jasmine async. A small example would be very useful.

like image 481
Androidewbie Avatar asked Sep 30 '13 08:09

Androidewbie


1 Answers

This is a nice setup for inserting modules with mocked dependencies into tests. This is an end to end example to get someone started, skip to the end if you just want to see the spec.

Folder Structure:

Jasmine
|-lib
||-jasmine          -- Contains all the Jasmine files
|||-boot.js
|||-jasmine-html.js
|||-jasmine.js
|||-jasmine.css
|||-jasmine_favicon.png
|||-...
|-spec              -- Spec files go here
||-hello-spec.js
|SpecRunner.html
|SpecRunner.js
|squire.js
Scripts
|knockout.js
|require.js
|jquery.js
App
|-hello.js
|-foo.js

At the time of your question, Jasmine 1.3 was the latest version. Since then 2.0 was released and contains some async improvements. Both versions are included here, 1.3 is commented out.

SpecRunner.html:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Hello Spec Runner</title>

    <link rel="shortcut icon" type="image/png" href="lib/jasmine/jasmine_favicon.png">
    <link rel="stylesheet" type="text/css" href="lib/jasmine/jasmine.css">
</head>
<body>
    <!-- Load RequireJS & the testsuite -->
    <script src="../Scripts/require.js" data-main="SpecRunner.js" type="text/javascript" ></script>
</body>
</html>

SpecRunner.js:

This question Does Jasmine 2.0 really not work with require.js? was helpful in getting this up and running.

(function () {
    'use strict';

    // Configure RequireJS to shim Jasmine
    requirejs.config({
        baseUrl: "../App",
        paths: {
            'jasmine' : '../Jasmine/lib/jasmine/jasmine',
            'jasmine-html': '../Jasmine/lib/jasmine/jasmine-html',
            'boot': '../Jasmine/lib/jasmine/boot', // This is not present in Jasmine 1.3
            'spec' : '../Jasmine/spec',
            'squire': '../Jasmine/squire',
            'knockout': '../Scripts/knockout-2.3.0',
            'jquery': '../Scripts/jquery-1.10.2' // This only used in the Jasmine 1.3 case.
        },
        shim: {
            'jasmine': {
                exports: 'jasmine'
            },
            'jasmine-html': {
                deps: ['jasmine'],
                exports: 'jasmine'
            },
            'boot': {
                deps: ['jasmine', 'jasmine-html'],
                exports: 'jasmine'
            },
            "squire": {
                exports: "squire"
            }
        }
    });

    // Define all of your specs here. These are RequireJS modules.
    var specs = [
      'spec/hello-spec'
    ];

    // Load Jasmine - This will still create all of the normal Jasmine browser globals unless `boot.js` is re-written to use the
    // AMD or UMD specs. `boot.js` will do a bunch of configuration and attach it's initializers to `window.onload()`. Because
    // we are using RequireJS `window.onload()` has already been triggered so we have to manually call it again. This will
    // initialize the HTML Reporter and execute the environment.
    require(['boot'], function () {

        // Load the specs
        require(specs, function () {

            // Initialize the HTML Reporter and execute the environment (setup by `boot.js`)
            window.onload();
        });
    });

    /******
     * Use this require if you're on Jasmine 1.3
     ******/
    //require(['jquery', 'jasmine-html'], function ($, jasmine) {
    //  var jasmineEnv = jasmine.getEnv();
    //  jasmineEnv.updateInterval = 1000;

    //  var htmlReporter = new jasmine.HtmlReporter();

    //  jasmineEnv.addReporter(htmlReporter);

    //  jasmineEnv.specFilter = function(spec) {
    //      return htmlReporter.specFilter(spec);
    //  };

    //  $(function() {
    //      require(specs, function(spec) {
    //          jasmineEnv.execute();
    //      });
    //  });
    //});

    // end 1.3
})();

foo.js:

define(['knockout'], function (ko) {

    var message = ko.observable("Go away, World!");

    return {
        message: message()
    };
});

hello.js:

define(['foo'], function (foo) {

    return {
        message : foo.message
    };

});

hello-spec.js:

define(['squire'], function (Squire) {

    var injector = new Squire();
    var builder = injector
        .mock('foo', {
                message: "Hello, World!"
        });

    describe('hello', function () {
        var hello;

        beforeEach(function (done) {
            // For async:
            // accept a "done" parameter and Jasmine will wait...
            builder.require(['hello'], function (hi) {
                hello = hi;
                done(); // ...until you invoke it.
            });

            /*******
             * Use the following if you're on 1.3
             *********/
            //var done;
            //runs(function () {
            //    builder.require(['hello'], function (hi) {
            //        hello = hi;
            //        done = true;
            //    });
            //});

            //waitsFor(function () {
            //    return done;
            //}, "Unable to load dependency not loaded", 750);

            // end 1.3
        });

        it('is welcoming', function() {
            expect(hello.message).toBe("Hello, World!");
        });
    });

    describe('hello no mock foo', function () {
        var hello;

        beforeEach(function (done) {
            // For async:
            // accept a "done" parameter and Jasmine will wait...
            require(['hello'], function (hi) {
                hello = hi;
                done(); // ...until you invoke it.
            });

            /*******
             * Use the following if you're on 1.3
             *********/
            //var done;
            //runs(function () {
            //    require(['hello'], function (hi) {
            //        hello = hi;
            //        done = true;
            //    });
            //});

            //waitsFor(function () {
            //    return done;
            //}, "Unable to load dependency not loaded", 750);

            // end 1.3
        });

        it('is less than welcoming', function () {
            expect(hello.message).toBe("Go away, World!");
        });
    });
});

Details

Lines

var injector = new Squire();
var builder = injector
    .mock('foo', {
        message: "Hello, World!"
    });

mock up hello's dependency on foo and then

builder.require(['hello'], function (hi) {
    hello = hi;
    done(); // ...until you invoke it.
});

loads the hello module using the mocked foo. Compare to using require itself to load hello in the second test:

 require(['hello'], function (hi) {
     hello = hi;
     done(); // ...until you invoke it.
 });

The Jasmine docs http://jasmine.github.io/ can fill you in on the "done()" functionality (2.0) or "runs/waitsFor" (1.3).

like image 57
dchanko Avatar answered Sep 22 '22 15:09

dchanko