Tikkes
Tikkes

Reputation: 4689

Front end javascript testing using Require and Resharper

So I've been trying to figure out how front end testing works (unit testing) but I am getting stuck on some point.

So I have my jasmine test set up as follows:

describe('Blabla', function () {

    it('returns true', function () {
        var people = require(["people"], function(ppl) {
            return ppl;
        });
        expect(people.getTitle()).toBe('People piolmjage');
    });
});

But running this gets me:

TypeError: undefined is not a funtion

So obviously, people is undefined. So perhaps my callback comes in too late. But if I remove the callback I get following error:

it('returns true', function () {
    var people = require("people");
    expect(people.getTitle()).toBe('People piolmjage');
});

Error: Module name "people" has not been loaded yet for context: _. Use require([])

I figure there is something wrong in my setup...Anyone have any idea how to get this FE testing to work?

I did manage to get it to work from console and using define combined with phantomjs and the durandal test files but I need this to work outside of the console and hereby I cannot use this define because the test runner won't find my tests.

That's why I need to use the CommonJS was of getting the required viewmodels.

people model

define([],
function () {
    var getTitle = function() {
        return "hello";
    }

    var peopleViewModel = {
        title: 'People page',
        getTitle: getTitle
    };
    return peopleViewModel;
});

UPDATE

I got the code working but not with resharper. Following this page from the durandal webpage.

But this gets me console output which is way to unstructured to actually read through.

I can however use the define keyword and then it works fine. So I assume it is the require keyword where I mess up something?

UPDATE 2

So I used fiddler to check what is going on. I also finally got it working (kinda...).

My testfile looks like this now:

///<reference path="../../Scripts/require.js"/>
///<reference path="../../test/lib/jasmine-2.1.3/jasmine.js"/>

///<reference path="../../App/viewmodels/people.js"/>

describe('Blabla', function () {
    it('require test', function (done) {
        require(['people'], function (people) {
            expect(people.title).toBe('People page');
            done();
        });
    });
});

And then I changed my people file:

define("people", ["bla"], function (bla) {
    return {
        title: 'People page',
        bla: bla
    };
});

As you can see here, I name my viewmodel to be people. This works for the testrunner but he doesn't actually get any files through requireJS but only the reference paths. Also this does not fit my needs because the durandal models are unnamed.

Fiddler screenshot:

Fiddler screenshot

So basically he does not use requireJS to get the viewmodels and therefor I cannot just use the require.config initializer to get to my viewmodels folder and download every viewmodel using requireJS. Any thoughts?

Upvotes: 1

Views: 377

Answers (2)

Tikkes
Tikkes

Reputation: 4689

I finally got it working, took me like a day and a half.

Anyway I don't use resharper anymore, or it's test runner to be more precise. Chutzpah is the one I turned to in the end. This too took me some research but I got it to the point where it includes everything as I want it to.

Check this post for sure

Here is what I did:

My people.js looks like this:

define(['viewmodels/bla'], function (bla) {
    return {
        title: 'People page',
        bla: bla //testing dependencies on other viewmodels
    };
});

Then I also made a bla.js

define(function() {
    return {
       bla: "bla"
    };
});

And now for the tests:

describe('Blabla', function () {
it('require test', function (done) {
    require(['viewmodels/people'], function (people) {
        expect(people.title).toBe('People page');
        done();
    });
});

it('dependency on require test', function (done) {
    require(['viewmodels/people'], function (people) {
        console.log(people.bla);
        expect(people.bla.bla).toBe('bla');
        done();
    });
});
});

And then eventually, reading the answers on the link provided on top I had to create a Chutzpah config file to create a test harnass:

{
   "Framework": "jasmine",
   "TestHarnessReferenceMode": "AMD",
   "TestHarnessLocationMode": "SettingsFileAdjacent",
   "References" : [
       {"Path" : "../Scripts/require.js" }, 
       {"Path" : "requireConfig.js" }
   ],
   "Tests" : [
     {"Path": "specs"}
   ]
}

Now, running the tests with Visual studio test runner actually gets me everything I need and as you can see, I can now access all my viewmodels through require like so: require(['viewmodels/whateverviewmodel'], function(whateverviewmodel){....})

I hope this answer can get people on their way to testing your (Durandal)SPA using Jasmine and RequireJS.

I know my viewmodels in this answer, nor in the question itself, say much but this should get you an idea of how to go about all of this.

Small Edit

You can now also skip the callback mess with require([]... inside of the tests and build your tests like you do your viewmodels with define

define(['viewmodels/people'], function (people) {
    describe('Blabla', function () {
        it('require test', function () {
            expect(people.title).toBe('People page');
        });

        it('dependency on require test', function () {
            console.log(people.bla);
            expect(people.bla.bla).toBe('bla');
        });
    });
});

This gets you less indents and is more readable in itself.

Upvotes: 2

Louis
Louis

Reputation: 151401

The require call provided by RequireJS is inherently asynchronous so you need to do something like this:

it('returns true', function (done) {
    require(["people"], function(people) {
        expect(people.getTitle()).toBe('People piolmjage');

        done(); // Signal that the test is done.
    });
});

The first attempt you show in your question cannot work. That's the classical "trying to return values synchronously form asynchronous code" mistake. The second attempt with require("people") does not work either because this require call is pseudo-synchronous and will work only if the module requested is already loaded. See this answer for an explanation of how this pseudo-synchronous require works.

Upvotes: 0

Related Questions