JQuery: Continuous Integration with QUnit, MSTest and WatiN

Background

We’re in the initial stages of a new project here at iMeta that looks likely to have a large proportion of logic in the client browser rather than the web server. JQuery is the ideal tool for the job here and it just so happens that I’m a huge fan of it too!

Before the project gets properly underway I want to experiment with unit testing JQuery. My ultimate aim being to develop a pattern for including JQuery unit tests in CI builds – with this achieved I can add real value to the project going forward.

After a bit of research I came across the following great blog post, it’s a worth a read as a primer for the remainder of my post - http://www.lostechies.com/blogs/joshuaflanagan/archive/2008/09/18/running-jquery-qunit-tests-under-continuous-integration.aspx.

Joshua wrote his post in 2008, as a result WatiN’s codebase has since moved on. I also wanted to use MSTest rather than NUnit, purely because we will be running our builds on a Team Foundation Build server. This meant that I needed to find a replacement for NUnit’s IterativeTest add-in (or the more up to date TestCaseSource attribute) in MSTest. Another bit of research unearthed http://codeclimber.net.nz/archive/2008/01/18/How-to-simulate-RowTest-with-MS-Test.aspx – perfect!

Code

Pulling all this research together gave a nice end result, which I hope will benefit others.  You will probably notice that my JavaScriptTester class is much simpler – this is firstly because of advances in the WatiN codebase for finding page elements and secondly because I only check and report pass/failure for whole QUnit tests rather than for each individual assertion within that test. You could easily extend the below to report on individual QUnit assertions should your scenario require it.

JavaScriptTester.cs
  1. namespace QunitJack.Tests
  2. {
  3.     [TestClass]
  4.     public class JavaScriptTester
  5.     {
  6.         private IE _ie;
  7.  
  8.         [TestInitialize]
  9.         public void TestInitialize()
  10.         {
  11.             _ie = new IE();
  12.             _ie.ShowWindow(NativeMethods.WindowShowStyle.Hide);
  13.         }
  14.  
  15.         public TestContext TestContext { get; set; }
  16.  
  17.         [DeploymentItem("QunitJack.Tests\\JavaScriptTester.xml"), TestMethod]
  18.         [DataSource("Microsoft.VisualStudio.TestTools.DataSource.XML", "|DataDirectory|\\JavaScriptTester.xml", "Row", DataAccessMethod.Sequential)]
  19.         public void RunQUnitTests()
  20.         {
  21.             _ie.GoTo(string.Format("http://localhost:90/{0}", TestContext.DataRow["Filename"]));
  22.             _ie.WaitForComplete(5);
  23.  
  24.             AssertQUnitTestResults();
  25.         }
  26.  
  27.         private void AssertQUnitTestResults()
  28.         {
  29.             var liElements = _ie.ElementsWithTag("li").Filter(x => x.Parent.Id == "qunit-tests");
  30.             foreach(var liElement in liElements)
  31.             {
  32.                 Assert.IsTrue(liElement.ClassName == "pass", liElement.DomContainer.ElementsWithTag("strong")[0].Text);
  33.             }
  34.         }
  35.     }
  36. }

JavaScriptTester.xml
  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <Rows>
  3.   <Row>
  4.     <Filename>Test1.htm</Filename>
  5.   </Row>
  6.   <Row>
  7.     <Filename>Test2.htm</Filename>
  8.   </Row>
  9. </Rows>

There are a couple of great QUnit blog posts over at http://indomitablehef.com/?cat=35, I have made use of the static Assert methods and dynamically setting up/tearing down the DOM.

Test1.htm
  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
  2. <html>
  3. <head>
  4.     <link rel="stylesheet" href="http://github.com/jquery/qunit/raw/master/qunit/qunit.css" type="text/css" />
  5.     <script type="text/javascript" src="Scripts/jquery-1.4.1.js"></script>
  6.     <script type="text/javascript" src="http://github.com/jquery/qunit/raw/master/qunit/qunit.js"></script>
  7.     <script type="text/javascript" src="Scripts/Assert.js"></script>
  8.     <script type="text/javascript" src="Scripts/DOMSetup.js"></script>
  9.     <script type="text/javascript" src="Scripts/DOMTearDown.js"></script>
  10.  
  11.     <script type="text/javascript">
  12.         $(document).ready(function() {
  13.        
  14.             test("renderJsonGridTests", function() {
  15.  
  16.                 DOMSetup('<div id="target" />');
  17.  
  18.                 var json = jQuery.parseJSON('{ "tableId" : "foo" }')
  19.                 $("div#target").html('<table id = "' + json.tableId + '"></table>');
  20.  
  21.                 Assert.AreEqual(true, $("#target > table").length == 1, "table exists");
  22.                 Assert.AreEqual("foo", $("#target > table").attr("id"), "table has correct id");
  23.  
  24.                 DOMTearDown("div#target");
  25.                 
  26.             });
  27.  
  28.         });
  29.     </script>
  30.  
  31. </head>
  32. <body>
  33.     <h1 id="qunit-header">
  34.         QUnit example</h1>
  35.     <h2 id="qunit-banner">
  36.     </h2>
  37.     <h2 id="qunit-userAgent">
  38.     </h2>
  39.     <ol id="qunit-tests">
  40.     </ol>
  41. </body>
  42. </html>

 

Summary

I’m sure that the above approach to our JQuery CI will evolve as the project does – but as an initial pattern I think it will do the job. Any refinements that I make will be added to this post somehow.

Comments

# re: JQuery: Continuous Integration with QUnit, MSTest and WatiN
Gravatar I'm with you all the way with your methods of testing. I wrote a lot of javascript in my last project when rendering job schedules to screen but because timescales were tight I didn't write unit tests. I still think that was the correct decision (I mocked out the json and tested javascript solely in the browser without any need for the back-end) however I would do it differently next time. With that in mind, and when there's support for jQuery, I'll probably have a bash using Script# (http://projects.nikhilk.net/ScriptSharp) - it let's you write C# code that's compiled in Javascript so you have all the tools and resources available in Visual Studio whilst developing the code such as my much beloved Resharper :) Now if only the full weight of Microsoft would get behind it and develop it further!
Left by Phil Hendry on 8/5/2010 1:20 AM

Leave Your Comment

Title*
Name*
Email (never displayed)
 (will show your gravatar)
Url
Comment*

Please add 1 and 1 and type the answer here:

Preview Your Comment.