Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Check NUnit test results from a batch script

4.14/5 (4 votes)
6 Jul 2009CPOL1 min read 38.7K   351  
A small program which allows to check results generated by the nunit-console utility.

Problem overview

If you use NUnit, you can run tests using the nunit-console utility from your build script. But, this test runner just shows the results in a console window and saves a report in an XML file. There is no functionality which allows to check the results of testing after the nunit-console utility execution and perform some actions depending on these results. Of course, there are some powerful build tools which allow to do it, for example, NAnt or FinalBuilder. But, what should you do if you still use simple .bat files to build your product? The most significant problem: how could you stop the build process if some Unit Test failed?

Proposed solution

The CheckTestResults utility helps to solve this issue. It can analyze the TestResult.xml file produced by the nunit-console utility, show all the found failures, sound a beep for each failure, and finally, stop execution and wait for user input if there was at least one failure during the testing.

The utility is written in C#, and requires .NET 2.0 (or higher). Here is the source code:

C#
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;

namespace CheckTestResults {
    class CheckTestResultsMain {

        class ErrorItem {
            public string Name;
            public string Message;
            public string StackTrace;
        }

        private static bool pauseOnError = true;
        private static string resultFileName = "";
        private static List<ErrorItem> errors;

        static void Main(string[] args) {

            Console.WriteLine("Check NUnit test results" + 
                    " utility version 1.0.0");
            Console.WriteLine("");

            errors = new List<ErrorItem>();


            if (args.Length > 0) {
                // Load program arguments
                for (int i = 0; i < args.Length; i++) {
                    if (args[i].StartsWith("-pause:")) {

                        string s = args[i].Substring("-pause:".Length);
                        pauseOnError = bool.Parse(s);
                    }
                    else
                        resultFileName = args[i];
                }
            }
            else {
                Console.WriteLine("Usage: CheckTestResults.exe" + 
                        " <path to TestResult.xml file>");
                return;
            }


            Console.WriteLine("Processing " + 
                    resultFileName + "...");
            Console.WriteLine("");
            try {
                //Analize test results file
                FileStream fstream = new FileStream(resultFileName, 
                           FileMode.Open, FileAccess.Read);
                try {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.Load(fstream);
                    LoadTestResults(xmlDoc.DocumentElement);
                }
                finally {
                    fstream.Close();
                }
            }
            catch (Exception ex) {
                Console.ForegroundColor = ConsoleColor.Red;

                Console.WriteLine("Error occur: ");
                Console.ForegroundColor = ConsoleColor.Magenta;
                Console.WriteLine(ex.Message);
                Console.Beep();
                Console.ForegroundColor = ConsoleColor.Gray;
                Console.WriteLine("");
                Console.WriteLine("Press any key to continue...");
                Console.ReadKey(true);
                return;
            }


            if (errors.Count > 0) {
                Console.ForegroundColor = ConsoleColor.Red;

                //Print errors
                Console.WriteLine("Tests failed");
                Console.WriteLine("");

                for (int I = 0; I < errors.Count; I++) {
                    Console.ForegroundColor = ConsoleColor.Magenta;
                    Console.WriteLine("  " + (I + 1).ToString() + 
                            ": " + errors[I].Name);
                    Console.ForegroundColor = ConsoleColor.Cyan;
                    Console.Write(errors[I].Message);
                    Console.ForegroundColor = ConsoleColor.DarkYellow;
                    Console.Write(errors[I].StackTrace);

                    Console.Beep();
                    Console.WriteLine("");
                    Console.WriteLine("");
                }

                //Stop and wait for user input
                Console.ForegroundColor = ConsoleColor.Gray;
                Console.WriteLine("");
                Console.WriteLine("Press any key to continue...");
                Console.ReadKey(true);
            }
            else {
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine("Testing succeeded!");
                Console.ForegroundColor = ConsoleColor.Gray;
                Console.WriteLine("");
            }
        }

        private static void LoadTestResults(XmlElement rootNode) {
            foreach (XmlNode node in rootNode.ChildNodes) {
                if (node is XmlElement && 
                         node.LocalName == "test-suite")
                    LoadTestSuite((XmlElement)node);
            }
        }

        private static void LoadTestSuite(XmlElement suiteNode) {
            foreach (XmlNode node in suiteNode.ChildNodes) {
                if (node is XmlElement && 
                       node.LocalName == "results") {
                    foreach (XmlNode subNode in node.ChildNodes) {
                        if (subNode is XmlElement ) {
                            if (subNode.LocalName == "test-suite") {
                                LoadTestSuite((XmlElement)subNode);
                            }
                            else if (subNode.LocalName == "test-case") {
                                LoadTestCase((XmlElement)subNode);
                            }
                        }
                    }
                }
            }
        }

        private static void LoadTestCase(XmlElement caseNode) {
            foreach (XmlNode node in caseNode.ChildNodes) {
                if (node is XmlElement && 
                        node.LocalName == "failure") {
                    LoadTestFailure((XmlElement)node);
                }
            }
        }

        private static void LoadTestFailure(XmlElement failureNode) {
            ErrorItem error = new ErrorItem();

            error.Name = 
              failureNode.ParentNode.Attributes["name"].Value;

            foreach (XmlNode node in failureNode.ChildNodes) {
                if (node.LocalName == "message") {
                    error.Message = node.FirstChild.Value;
                }
                else if (node.LocalName == "stack-trace") {
                    error.StackTrace = node.FirstChild.Value;
                }
            }

            errors.Add(error);
        }
    }
}

Using the code

To use CheckTestResults, just call it with one parameter: the path to the TestResult.xml file produced by nunit-console. So, your build script will look like:

nunit-console MyProject\Tests\bin\Debug\MyProjectTests.dll
CheckTestsResult.exe TestResult.xml

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)