Thursday 9 June 2011

Javascript tests in Continuous Integrated builds

We implemented this approach to incorporate QUnit in our regular CI builds a while back and I think it's worth sharing. Since QUnit runs in a browser, it's typically only viewed when someone can be bothered to run the tests manually. Possible options include launching the QUnit page as a post-build step, but that still requires eye balling the results, which means people can ignore it.

If we treat the JavaScript in our web applications with the respect it deserves since it's critical code, we must incorporate the results of the QUnit tests in out CI build result. We took the following approach to failing a build if a QUnit test fails.

Using a normal (NUnit) unit test, we load the QUnit page with WatiN. Once loaded, the JavaScript is executed and we parse the DOM to find tests and test failures. QUnit markup could be more helpful in this respect, but it's certainly far from impossible. Any failing tests are output to the console so that they appear in the CI build log. If any QUnit test has failed, the [NUnit] unit test Asserts a failure to ensure that the overall build is failed.

The thing I dislike most about this approach is that it's an integration test rather than a true unit test, with the parsing of the QUnit results tightly bound with the QUnit output DOM. But since we use a local copy of QUnit, we at least won't be stung by an update of QUnit that we weren't aware of.

A few things I like very much about this technique are:

  • The ability to run the tests as part of the CI build. Any commit of code verifies the tests still pass.
  • The ability to run the tests through a number of different browsers thanks to WatiN.
  • The treatment of JavaScript code as equally important and "grown-up" as our server side (C#) code.

I could probably improve matters by changing the testPageUrl. The code below assumes a local virtual directory called "Tests" has been set up and configured for my project. Without further ado, here's the code:

 [TestFixture]
 public class JavaScriptTests
 {
  [Test]
  public void LoadJavaScriptTestPage()
  {
   const string testPageUrl = "http://localhost/Tests/JavaScript%20Tests.htm";
   string jsTestResults;
   string jsFailedTestResults;
   using (var browser = new IE(testPageUrl))
   {
    browser.ClearCache();
    ParseQUnitTestResults(browser, out jsTestResults, out jsFailedTestResults);
   }
   Trace.WriteLine(string.Format("{0}:{1}", testPageUrl, jsTestResults));
   if (jsTestResults.IndexOf('F') >= 0)
   {
    Assert.Fail(jsFailedTestResults);
   }
  }

  private void ParseQUnitTestResults(IElementContainer browser, out string jsTestResults, out string jsFailResults)
  {
   var testResults = new StringBuilder();
   var failResults = new StringBuilder();
   foreach (var element in browser.ElementsWithTag("li").Where(element => element.Parent != null && element.Parent.Id != null && element.Parent.Id.Equals("qunit-tests")))
   {
    if (element.ClassName.Equals("fail", StringComparison.InvariantCultureIgnoreCase))
    {
     testResults.Append("F");
     failResults.AppendLine(element.Text);
    }
    else
    {
     testResults.Append(".");
    }
   }
   jsTestResults = testResults.ToString();
   jsFailResults = failResults.ToString();
  }
 }

Thursday 2 June 2011

PhoneGap Experiences

My first encounter and use of PhoneGap was an eye opener. The ability to produce a native app for my Android phone, AND other devices, just blew me away. Best of all, I didn't need to learn Objective-C, Java, or any other new language to produce these apps.

The apps are web apps. I'm not going to pretend you can do everything as easily in a web app as you can in native code; it certainly has a few down sides. But there is a lot that comes built in with web browsers, and you can lean on that functionality to build apps very quickly indeed.

I first started building these apps using the combination of Eclipse, JDK and ADK. Not having used any of these tools before, it was a learning curve, but not at all steep. See my post on Setting up PhoneGap for Android on Windows 7. The installation is the most arduous part of the development process.

Then along came PhoneGap Build which I loved from the start and I blogged about it in PhoneGap Build is amazing. Since then there have been a few things that have niggled me (hey, it's still in Beta you know!) and I've considered going back to my Eclipse builds.

One thing that might not be immediately obvious, is that you don't need to use PhoneGap (the library) to use their build server. Their build service simply builds your app regardless of which libraries you use. And for some (simple) apps, there is no need for PhoneGap. If your app doesn't need any device interaction, you can get by with a plain web page and JS functionality. This was a surprising realisation on my part when I wrote a simple lock game that only used a little JavaScript and some CSS animations.

The few things that niggle me with PhoneGap build are as follows (in no particular order):

  • Seems to ignore which devices you want to build for and builds them all anyway. (The one I want is not first, so it bugs me.)
  • The builds are quick, but not knowing how long they'll take, or when they'll start when they're queued is a little frustrating.
  • There doesn't seem to be a way of defining a splash screen image. (Edit: it's actually very easy to define a splash screen in the configuration XML file.) There is a way to add a splash page when building with Eclipse.
  • I've not found good documentation on the options and schema of the configuration XML file. Maybe I've not looked hard enough.

But I can ignore all of the above because of the reasons I explained in my post on how amazing it is. There's certainly a pleasure in knowing all you need is a web browser and internet access to get an app created and built. It does help to have Git installed locally and edit and test the files locally before pushing changes and rebuilding, but that's a simple install and low entry bar for what we can achieve with it.

One of the most desirable gaps that PhoneGap wasn't bridging for me personally, was the menu device button on Android. The latest release now includes support for all the device buttons. There were a few posts floating around the Internet on how to hack it into your app yourself (if you were on the Eclipse build path, no luck if you used PhoneGap build). But now that it's in the PhoneGap library, I think it has everything I need.

"I can wholeheartedly recommend PhoneGap as a library to bridge the device-JavaScript gaps."

I think PhoneGap is my library of choice for mobile apps. I have to admit that I've not tried any others - why would I? PhoneGap bridges all the device features I want and need, and then quite a few that I don't. I can wholeheartedly recommend it as a library to bridge the device-JavaScript gaps.

In terms of developing JavaScript web apps as native apps, there are issues that I've found troublesome but can be overcome. These are not as a result of using PhoneGap; they're a result of creating apps as browser based apps. Most commonly this boils down to layout issues, screen size limitations, and differences between the phone's browser and the Chrome browser I use on my desktop for development.