Click here to Skip to main content
15,881,173 members
Articles / Programming Languages / C#

Check NUnit test results from a batch script

Rate me:
Please Sign up or sign in to vote.
4.14/5 (4 votes)
6 Jul 2009CPOL1 min read 38K   351   15   7
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)


Written By
Founder Korzh.com
Ukraine Ukraine
Software developer and entrepreneur.

Main projects:
* EasyQuery - ad-hoc data filtering UI for .NET applications;
* Localizer - localization tool kit for Delphi projects;

Comments and Discussions

 
QuestionHow do I use the Category attribute with nunit-console.exe? Pin
Xarzu16-Jun-16 12:17
Xarzu16-Jun-16 12:17 
AnswerRe: How do I use the Category attribute with nunit-console.exe? Pin
PIEBALDconsult16-Jun-16 12:24
mvePIEBALDconsult16-Jun-16 12:24 
GeneralMy vote of 4 Pin
Kanasz Robert28-Sep-12 5:55
professionalKanasz Robert28-Sep-12 5:55 
QuestionHave you tried just checking the return code of nunit-console? Pin
erik.f14-Feb-10 4:42
erik.f14-Feb-10 4:42 
GeneralMain should return the status Pin
bzchai8-Jul-09 2:55
bzchai8-Jul-09 2:55 
GeneralRe: Main should return the status Pin
Sergiy Korzh9-Jul-09 14:31
professionalSergiy Korzh9-Jul-09 14:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.