Send mail to the author(s)

August 21, 2007

HOWTO: Create unit tests for device apps using NUnitLite 0.1.0

Charlie Poole has released NUnitLite v0.1.0, a streamlined, embeddable version of the NUnit unit testing framework. The big news for device developers is that it supports the .NET Compact Framework -- v1.0, v2.0 and v3.5 -- out of the box. There are a couple of caveats though. One is a bug in CommandLineOptions.cs (line 132) which incorrectly references the conditonal compiler symbol, WindowsCD. This should be WindowsCE. Charlie has just fixed this so you might want to get the latest code rather than the 0.1.0 release. The other caveat is that there is no console on Pocket PCs and Smartphones, so you need to work around this to see the results. I'll go into this in detail below.

Creating unit tests for device applications

There are two ways in which you can do this since NUnitLite is a source-only distribution. That is, when you download NUnitLite you are downloading the source code. There are no pre-compiled versions.  

The first option is to add the NUnitLite project as a separate project to your solution. This isn't so simple for device projects as the NUniteLite Visual Studio project targets to the .NET Framework. The easiest way to add NUnitLite as a separate project to your solution is as follows:

  • Add the NUnitLite project to your solution.
  • Create a new Windows CE C# class library project and call it NUnitLiteCF. You could create a Pocket PC or Smartphone project, but since these platforms are based on Windows CE, you can use the Windows CE project to target vanilla CE devices as well as Smartphones and Pocket PCs.
  • Delete Class1.cs from the NUnitLiteCF project as it is not needed.
  • Drag the following folders from the NUnitLite project to the NUnitLiteCF project:
    • Constraints
    • Framework
    • Runner
  • Drag the Env.cs from NUnitLite to NUnitLiteCF.
  • Remove the NUnitLite project from your solution.
  • Add new a project Smart Device Console Application project. This can be VB or C#.
  • Add a reference to the NUnitLiteCF project to the Console Application.

The second option is to add the NUnitLite source code directly to your C# Smart Device Console Application project.

In both cases, you will need to add some to the Main method to kick start the test runner. Inside the Console Application's Main method, add the following:

[C#]
NUnitLite.Runner.ConsoleUI.Main(args);

[VB]
NUnitLite.Runner.ConsoleUI.Main(args)

Capturing test results on a Pocket PC or Smartphone

Since there is no console on a Pocket PC or Smartphone, you will need an alternative method of capturing the test results so you can review them. The easiest way is to redirect STDOUT so that calls to Console.WriteLine() are output to a different TextWriter instance. Using this method, it is very easy to create a log file of the test results, like this:

static void Main(string[] args)
{
    using (StreamWriter file = new StreamWriter("\\NUnitLite-Results.txt"))
    {
        TextWriter stdout = Console.Out;
        try
        {
            Console.SetOut(file);
            ConsoleUI.Main(args);
        }
        finally
        {
            Console.SetOut(stdout);
        }
    }
}

This will write the test results to NUnitLite-Results.txt in the root folder on the device. The contents of the file will look something like this:

NUnitLite version 0.1.0
Copyright 2007, Charlie Poole

Runtime Environment -
    OS Version: Microsoft Windows CE 5.1.195
  .NET Version: 3.5.7121.0

1 Tests : 0 Errors, 1 Failures, 0 Not Run

Errors and Failures:

1) Test (MyNamespace.MyAppsTests.AssertFailTest)
test
  at NUnit.Framework.Assert.Fail(String message, Object[] args)
  at MyNamespace.MyAppsTests.AssertFailTest()
  at System.Reflection.RuntimeMethodInfo.InternalInvoke(RuntimeMethodInfo rtmi, Object obj, BindingFlags invokeAttr, Binder binder, Object parameters, CultureInfo culture, Boolean isBinderDefault, Assembly caller, Boolean verifyAccess, StackCrawlMark& stackMark)
  at System.Reflection.RuntimeMethodInfo.InternalInvoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean verifyAccess, StackCrawlMark& stackMark)
  at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
  at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
  at NUnitLite.ProxyTestCase.InvokeMethod(MethodInfo method, Object[] args)
  at NUnit.Framework.TestCase.RunTest()
  at NUnit.Framework.TestCase.RunBare()
  at NUnit.Framework.TestCase.Run(TestResult result, TestListener listener)
  at NUnit.Framework.TestCase.Run(TestListener listener)
  at NUnit.Framework.TestSuite.Run(TestListener listener)
  at NUnit.Framework.TestSuite.Run(TestListener listener)
  at NUnitLite.Runner.TestRunner.Run(ITest test)
  at NUnitLite.Runner.ConsoleUI.Run(ITest test)
  at NUnitLite.Runner.TestRunner.Run(Assembly assembly)
  at NUnitLite.Runner.ConsoleUI.Run()
  at NUnitLite.Runner.ConsoleUI.Main(String[] args)
  at OpenNETCF.Linq.Demo.Program.Main(String[] args)

 As you can see, I'm running this on a Windows Mobile 5.0 device using .NET Compact Framework 3.5.