views:

503

answers:

9

I want to test the JavaScript code in our web app. We are using the jQuery library and .Net MVC. I've looked at these answers, and jqUnit looked nice, so I tried it, just to realize that I'll have to pretty much recreate all markup for every page I want to test.

Am I missing something? Are there any alternative approaches for testing JS/jQuery code? Our markup is at times complex, and changes drastically as a result of AJAX calls.

The best I can think of is trying to stick our app in an iframe on a test page. Haven't tried it yet.

+4  A: 

If your testing javascript functions/objects, might i suggest YUI's testing component.

http://developer.yahoo.com/yui/yuitest/

Very similar setup to JUnit but only requires that you include a few test javascript files. It would be pretty easy to include this in your page while in a Test mode only.

Zoidberg
Thanks! Will try this out as soon as possible.
dalbaeb
+1  A: 

Testing your app inside an iframe could work.

What I've found to be better is to stick with the Model-View-Presenter (MVP) paradigm (on the client side alone) along with breaking the UI components into digestible chunks to be able to test each component inside its own "driver" page. Most of the logic resides inside the Presenter(s), which is pure JavaScript and therefore can be tested with pure JavaScript unit tests. Individual UI components can be tested with a framework like Selenium and their testing would simply be ensuring that they throw the right events to the Presenter. The Model can also be made testable as pure-JavaScript if the backend access part is stubbed/mocked out into some sort of Fetcher class.

Ates Goral
This is quite theoretical. Is there a tutorial/example? And where does the markup fit in when testing "each component inside its own 'driver' page"? Does that mean that I will have to separate the markup into chunks for each page? I assume that by "'driver' page" you mean a page that includes JS test code and displays the test results.
dalbaeb
+2  A: 

It might be worth considering Selenium and running some automated tests at the browser level.

Of course, in an ideal world you would work with a unit-testing framework at the code-level, but given that may involve a lot of up-front rework, a solid compromise might be to test that the javascript actually does what you need it to using Selenium or a similar framework.

Toby Hede
A: 

The visibone browser reference suggests something they call 'assertiveness'. It basically gives a suggestion on how to implement unit testing for JavaScript. Its really quite simple.

See the bottom right of the following picture link:

http://www.visibone.com/products/ebk8-9%5F850.jpg

To summarise here:

Create a function assert():

function assert(fact, details){
    if (!fact) alert("Assert failure: " + details);
}

And you could call it something like this:

assert((parseInt("1") == 1), "Check string '1' is equal to integer 1");

Or possibly with a better === comparison:

assert((parseInt("1") === 1), "Check string '1' is equal to integer 1");

There is more on the linked picture, and your assert functions could become more sophisticaed.

I did a google search on "JavaScript assert", and got quite a few links back, so it might be worth checking out yourself.

James Wiseman
+3  A: 

You could just create the structure you want to test in the memory without appending it to the document. This won't work will all the tests (as for example CSS propeties won't affect items not inserted to the document but in some cases this may be worth a try).

First it's very fast, and second you don't spoil your document with test items you need to remove after you complete the tests.

Here's that idea in a very simple example.

test('My tests', function () {
    var testSubj = $('<div>This <b>is</b> my <span>test subject</span></div>');

    ok(testSubj.children('span') == 'test subject', 'Span is correct');
});
RaYell
Wouldn't that defeat the purpose of trying to test the code with real markup? What if markup changes? The test will still pass and potential problems will go unnoticed.
dalbaeb
It all depends of what you are testing. As I said you won't be able to test all with that approach. But in some cases it's easier to go this way. For example if you are creating some complicated structure in a function and you want to test if it's correct, there's no reason to append it to the document.
RaYell
+1, this approach works great for me. It means structuring your functions/classes to rely less on a DOM structure, and to receive the relevant objects as parameters.
orip
+2  A: 

You may need two approaches, depending on the code:

  1. Code that can be unit tested. There is some Javascript that is independent enough that you can use JSUnit to test it with a minimal amount of HTML markup. In a way this markup is your test fixture data.

You may want to figure out how you can restructure your code such that it can be tested in this way. Can you make the JS more modular, or less dependent on a certain markup? (Sadly, sometimes you can't.)

  1. Code that requires integration testing. Like you said, it's a pain to recreate all that markup. Someone else suggested selenium (which runs your page in a frame). That's a good approach to test Javascript code in its real environment.

The disadvantage of these is that they tend to be slower to run and a little harder to maintain or brittle. Look up the "Page Object" pattern to help you out on the maintainability front.

We end up automating the JS unit tests using selenium to drive it.

ndp
actually you can make your unit-tests completely independent of the browser if you want. It's a lot less work to mock JQuery, or even Browser DOM functions than most people think. Especially considering the mocks only have to support the use case your are actually testing. Running the unittests outside of the browser using Rhino, V8, or even some other javascript engine helps reduce the chance the browser will interfere with your mocking.The correct place to test JQuery itself is in an integration or regression test. But unit tests don't need to test JQuery.
Jeremy Wall
+1  A: 

You can use a template engine. Many different template engines exist for many different http server setups, so you'll probably find one that suits your needs, and that is implemented in the (server side) programming language of your choice.

Now once you installed your template engine, you can use your original layout and add a little marker, where you can insert a <script> tag when testing, and nothing when not testing.

The advantage of this approach: there needs to be absolutely no difference between the production pages and the test pages. You can avoid adding an iframe you don't need for anything else than testing.

lbp
+1  A: 

Windmill is an excellent python based javascript test framework. It's under active development, so it's constantly improving. I would recommend taking a look at it. You can create automated tests it has a nice GUI you also can use to setup tests. There's a bunch of other features, but no reason to list them all here.

googletorp
+1  A: 

So there are two kinds of tests you seem to be asking about. One is unittesting the code you write that consumes jquery. The answer to this is much simpler than everyone usually thinks. Just mock jquery in your unit tests. It's really easy and works pretty much the same way you would do mocking in any other programming language. Except it's easy to do without even a framework in javascript. I actually run these kinds of tests in an automated test suite using Test.TAP and rhino for my projects. No browser required.

The other kind of test is an integration or regression test. This is where a tool like selenium comes in. These tests happen in your actual application and are designed to make sure that all the pieces work together the way your expect. Browser, JQuery, your javascript, and Web-Server. Keeping these two kinds of tests seperate in your mind is key to putting together a test suite for javascript code. Ideally you would run these tests in multiple browsers (possibly even multiple configurations of the browsers).

Jeremy Wall
Agreed. Except that I'm only slightly familiar with mocking (from researching for this task), and so I can neither imagine, nor have I ever seen, an example of mocking jQuery. I would appreciate any pointers.
dalbaeb
Example:function $() { return "foo";}$('<div>') now returns "foo".Simple as that. Since JQuery actually returns objects for chaining you would need to mock that instead of just returning a string but that's the basic idea.
Jeremy Wall