Parsing JUnit/XML report file to email results

Best practices, code snippets for common functionality, examples, and guidelines.
Darwin
Posts: 26
Joined: Tue Jul 25, 2017 9:00 pm

Parsing JUnit/XML report file to email results

Post by Darwin » Tue Apr 10, 2018 6:37 pm

Just sharing a parser I wrote to email the results of our nightly automation suite runs to our team. Not everyone who gets the email will necessarily have or want the Ranorex report viewer on their PC (or other device) they use to check email, but I wanted them all to see the basic results and stats from the nightly run.

I have our test suite run and generate both the original Ranorex report and the Junit/XML report, then run this utility to parse the XML report and write a summary to an HTM file. Next, I run this freeware SendItQuiet utility to grab the HTM file as the email body and distribute the email. The report looks like the abbreviated screenshot below.
ResultsUploader.png
ResultsUploader.png (36.66 KiB) Viewed 540 times

Code: Select all

using System;
using System.Xml;

namespace RanorexResultUploader
{
    class Program
    {
        /// <summary>
        /// Reads and parses the Ranorex XML/JUnit report to get stats for overall test suite execution and
        /// individual test case results, then writes this information to an HTM file to be included in the 
        /// body of the results email (a subsequent call to the SendItQuiet email utility grabs the file).
        /// </summary>
        /// <param name="args">
        /// [0] - The original Ranorex report file, e.g.: HOSTNAME_YYYY_MM_DD_HH_NN_SUITENAME.rxlog
        /// [1] - The build number of the installed AUT, e.g.: 1.4.2.32
        /// </param>
        static void Main(string[] args)
        {
            XmlDocument resultsFile = new XmlDocument();

#if DEBUG
            // For local debugging (replace with actual file)
            string reportFile = @"\\<share>\<hostname>_2018_01_22_9_09_SelectedTest.rxlog";
            string buildNumber = "1.4.2.X";
            resultsFile.Load(reportFile + ".junit.xml");
#endif
            // ********************************
#if !DEBUG

            // For Release -- run-time parameters
            string reportFile = args[0];
            string buildNumber = args[1];
            resultsFile.Load(@"C:\Test\Reports\" + reportFile + ".junit.xml");
#endif

            using (System.IO.StreamWriter file = new System.IO.StreamWriter(@"C:\Test\Reports\EmailBody.htm"))
            {
                TestSuite currentTestSuite = new TestSuite();

                XmlNode testSuite = resultsFile.DocumentElement;
                currentTestSuite.Name = testSuite.Attributes["name"].Value;
                currentTestSuite.TestsRun = testSuite.Attributes["tests"].Value;
                currentTestSuite.Failures = testSuite.Attributes["failures"].Value;
                currentTestSuite.ExecutionTime = testSuite.Attributes["time"].Value;
                currentTestSuite.Skipped = testSuite.Attributes["skipped"].Value;
                currentTestSuite.TimeStamp = testSuite.Attributes["timestamp"].Value;
                currentTestSuite.HostName = testSuite.Attributes["hostname"].Value;

                file.WriteLine("<table border=\"1\">");
                file.WriteLine("<caption><b>{0}</b></caption>", currentTestSuite.Name);
                file.WriteLine("<tr><th>Build<td>{0}", buildNumber);
                file.WriteLine("<tr><th>Tests Run<td>{0}", currentTestSuite.TestsRun);

                if (currentTestSuite.Failures != "0")
                    file.WriteLine("<tr><th>Failed<td bgcolor=\"Red\">{0}", currentTestSuite.Failures);
                else
                    file.WriteLine("<tr><th>Failed<td bgcolor=\"Lime\">0");

                if (currentTestSuite.Skipped != "0")
                    file.WriteLine("<tr><th>Skipped<td bgcolor=\"Yellow\">{0}", currentTestSuite.Skipped);
                else
                    file.WriteLine("<tr><th>Skipped<td>0");

                file.WriteLine("<tr><th>Execution Time<td>{0}", currentTestSuite.ExecutionTime);
                file.WriteLine("<tr><th>Timestamp<td>{0}", currentTestSuite.TimeStamp);
                file.WriteLine("<tr><th>Host<td>{0}", currentTestSuite.HostName);
                file.WriteLine("</table><br>");

                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("Test Suite:");
                Console.WriteLine("\tName: {0}", currentTestSuite.Name);
                Console.WriteLine("\tTests Run: {0}", currentTestSuite.TestsRun);
                Console.WriteLine("\tFailures: {0}", currentTestSuite.Failures);
                Console.WriteLine("\tTests Skipped: {0}", currentTestSuite.Skipped);
                Console.WriteLine("\tExecution Time: {0}", currentTestSuite.ExecutionTime);
                Console.WriteLine();

                file.WriteLine("<table border=\"1\">");
                file.WriteLine("<tr><th>Test Case<th>Result<th>Duration<th>Error Msg");

                XmlNodeList testCases = resultsFile.DocumentElement.SelectNodes("/testsuite/testcase");
                foreach (XmlNode currentNode in testCases)
                {
                    TestCase testCase = new TestCase();

                    testCase.Name = currentNode.Attributes["name"].Value;
                    testCase.ExecutionTime = currentNode.Attributes["time"].Value;
                    testCase.ClassName = currentNode.Attributes["classname"].Value;

                    XmlNode skippedNode = currentNode.SelectSingleNode("skipped");
                    if (skippedNode != null)
                    {
                        testCase.SkippedMsg = skippedNode.Attributes["message"].Value;
                    }

                    XmlNode errorNode = currentNode.SelectSingleNode("error");
                    if (errorNode != null)
                    {
                        testCase.ErrorMsg = errorNode.Attributes["message"].Value;

                        XmlNode errorDesc = currentNode.SelectSingleNode("system-err");
                        if (errorDesc != null)
                        {
                            if (errorDesc.InnerText.Length > 1000)
                                testCase.ErrorDescription = errorDesc.InnerText.Substring(0, 1000);
                            else
                                testCase.ErrorDescription = errorDesc.InnerText;
                        }                       
                    }

                    Console.WriteLine("Test Case: {0}", testCase.Name);
                    Console.WriteLine("\tTime: {0}", testCase.ExecutionTime);

                    file.Write("<tr><td>{0}", testCase.Name);

                    if (testCase.Failed)
                    {
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine("\tFailed: {0}", testCase.ErrorMsg);
                        Console.WriteLine(testCase.ErrorDescription);
                        file.Write("<td bgcolor=\"Red\">FAILED");
                    }
                    else if (testCase.Skipped)
                    {
                        Console.ForegroundColor = ConsoleColor.Yellow;
                        Console.WriteLine("\tSkipped: {0}", testCase.SkippedMsg);

                        file.Write("<td bgcolor=\"Yellow\">SKIPPED");
                    }
                    else
                    {
                        Console.ForegroundColor = ConsoleColor.Green;
                        Console.WriteLine("\tPASSED!");

                        file.Write("<td bgcolor=\"Lime\">PASSED");
                    }
                    Console.ForegroundColor = ConsoleColor.White;
                    Console.WriteLine();

                    file.Write("<td>{0}", testCase.ExecutionTime);
                    file.Write("<td>{0}", testCase.ErrorDescription);
                    file.WriteLine();
                }
                file.WriteLine("</table>");
                file.WriteLine("<br><br>Complete Log File at:<br>");
                file.WriteLine(@"\\{0}\Reports\{1}", currentTestSuite.HostName, reportFile);
                file.WriteLine("</html></body>");
            }

#if DEBUG
            Console.ReadLine();
#endif
        }
    }

    class TestCase
    {
        public string Name;
        public string ClassName;
        public bool Skipped;

        private string _skippedMsg;
        public string SkippedMsg
        {
            get { return _skippedMsg; }
            set
            {
                Skipped = true;
                _skippedMsg = value;
            }
        }

        public bool Failed;

        private string _errorMsg = "";
        public string ErrorMsg
        {
            get { return _errorMsg;}
            set
            {
                Failed = true;
                _errorMsg = value;
            }
        }

        private string _errorDescription = "";

        public string ErrorDescription
        {
            get { return _errorDescription; }
            set
            {
                Failed = true;
                _errorDescription = value;
            }
        }

        public string ExecutionTime
        {
            get
            {
                int hours = _executionInSeconds / 3600;
                int minutes = (_executionInSeconds % 3600) / 60;
                int seconds = (_executionInSeconds % 3600) % 60;

                string returnValue = (hours > 0 ? hours.ToString() + "h " : "") + minutes.ToString() + "m " + seconds.ToString() + "s";
                return returnValue;
            }
            set { _executionInSeconds = Int32.Parse(value); }
        }

        private int _executionInSeconds = 0;


        public TestCase()
        {
            Skipped = false;
            Failed = false;
        }
    }

    class TestSuite
    {
        public string Name;
        public string TestsRun;
        public string Failures;
        public string Skipped;
        public string TimeStamp;
        public string HostName;

        public string ExecutionTime
        {
            get
            {
                int hours = _executionInSeconds / 3600;
                int minutes = (_executionInSeconds % 3600) / 60;
                int seconds = (_executionInSeconds % 3600) % 60;

                string returnValue = (hours > 0 ? hours.ToString() + "h " : "") + minutes.ToString() + "m " + seconds.ToString() + "s";
                return returnValue;
            }
            set { _executionInSeconds = Int32.Parse(value); }
        }

        private int _executionInSeconds = 0;

        
    }
}
Subsequent call to SendItQuiet from our overall test execution BAT file:

Code: Select all

call \Test\SendItQuiet\SendItQuiet.exe -s <sender> -port <port> -u %fromEmail% -protocol normal -p -f %fromEmail% -t %recipientList% -subject %emailSubject% -bodyfile %baseReportPath%\EmailBody.htm
Last edited by Darwin on Wed Apr 18, 2018 10:09 pm, edited 1 time in total.

User avatar
odklizec
Ranorex Guru
Ranorex Guru
Posts: 3930
Joined: Mon Aug 13, 2012 9:54 am
Location: Zilina, Slovakia

Re: Parsing JUnit/XML report file to email results

Post by odklizec » Wed Apr 11, 2018 7:21 am

Hi,

This is very cool! Thanks for sharing your code! ;)
Pavel Kudrys
Ranorex explorer at Descartes Systems

Please add these details to your questions:
  • Ranorex Snapshot. Learn how to create one >here<
  • Ranorex xPath of problematic element(s)
  • Ranorex version
  • OS version
  • HW configuration