Search code examples
c#xmlxunitxelement

Problem parsing through multiple elements in the XUnit XML file using XElement


I'm having trouble using XElement to parse multiple elements through an XUnit XML file and return the value.

Here is the XML File

<assemblies timestamp="07/31/2018 14:58:48">
  <assembly name="C:\Users\bf\Documents\Visual Studio 2015\Projects\xUnitDemo\xUnitDemo\bin\Debug\xUnitDemo.DLL" environment="64-bit .NET 4.0.30319.42000 [collection-per-class, parallel (1 threads)]" test-framework="xUnit.net 2.3.1.3858" run-date="2018-07-31" run-time="14:58:47" config-file="C:\Users\bf\Documents\Visual Studio 2015\Projects\xUnitDemo\packages\xunit.runner.console.2.4.0\tools\net452\xunit.console.exe.Config" total="15" passed="14" failed="1" skipped="0" time="0.257" errors="0">
    <errors />
    <collection total="2" passed="1" failed="1" skipped="0" name="Test collection for xUnitDemo.SimpleTests" time="0.070">
      <test name="xUnitDemo.SimpleTests.PassingTest" type="xUnitDemo.SimpleTests" method="PassingTest" time="0.0636741" result="Pass">
        <traits>
          <trait name="test" value="test" />
          <trait name="requirement" value="test" />
          <trait name="labels" value="test" />
        </traits>
      </test>
      <test name="xUnitDemo.SimpleTests.FailingTest" type="xUnitDemo.SimpleTests" method="FailingTest" time="0.0059474" result="Fail">
        <failure exception-type="Xunit.Sdk.EqualException">
          <message><![CDATA[Assert.Equal() Failure\r\nExpected: 5\r\nActual: 4]]></message>
          <stack-trace><![CDATA[ at xUnitDemo.SimpleTests.FailingTest() in C:\Users\smsf\documents\visual studio 2015\Projects\xUnitDemo\xUnitDemo\SimpleTests.cs:line 30]]></stack-trace>
        </failure>
      </test>
    </collection>
  </assembly>
</assemblies>

I'm able to parse through test element using this code.

private static List<TestResults> GetTestAutomationExecutionResult(string filePath)
{
    List<TestResults> testResults = new List<TestResults>();

    XElement xelement = XElement.Load(filePath);

    IEnumerable<XElement> results = xelement.Elements().Where(e => e.Name.LocalName == "test");
    
    foreach (var result in results)
    {
        if (result.Attribute("result").Value == "Fail")
        {
            testResults.Add(new TestResults(result.Attribute("result").Value, "this is where the failure message would go"));
        }
        else
        {
            testResults.Add(new TestResults(result.Attribute("result").Value, ""));
        }
    }

But I'm having a hard time trying to find and add message inside of failure element in the foreach.

 result.Attribute("message").Value

Solution

  • Your code has a couple problems:

    1. The <result> elements are not direct children of the root element, so xelement.Elements().Where(e => e.Name.LocalName == "test") does not select anything. You need to descend deeper into the hierarchy, e.g. with Descendants().

    2. The message text is contained in an indirect child element of the <test> node, specifically failure/message. You need to select this element to get the message.

      result.Attribute("message").Value will not work because the XElement.Attribute(XName) method selects an XML attribute rather than an element.

      See: XML attribute vs XML element.

    Putting those two points together, your code should look like:

    private static List<TestResults> GetTestAutomationExecutionResult(string filePath)
        => GetTestAutomationExecutionResult(XElement.Load(filePath));
    
    private static List<TestResults> GetTestAutomationExecutionResult(XElement xelement)
    {
        var query = from e in xelement.Descendants()
            where e.Name.LocalName == "test"
            let r = e.Attribute("result").Value
            let m = r == "Fail" ? e.Elements("failure").Elements("message").FirstOrDefault()?.Value : ""
            select new TestResults(r, m);
        return query.ToList();
    }
    

    Demo fiddle here.