Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add system-err and system-out to testcase #47

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 85 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ JUnit xml report extension for [Visual Studio Test Platform](https://github.com/
| ------ | -------------- | ------------------- |
| JUnit | [![NuGet](https://img.shields.io/nuget/v/JUnitXml.TestLogger.svg)](https://www.nuget.org/packages/JUnitXml.TestLogger/) | [![MyGet Pre Release](https://img.shields.io/myget/spekt/vpre/junitxml.testlogger.svg)](https://www.myget.org/feed/spekt/package/nuget/JunitXml.TestLogger) |

If you're looking for `Nunit`, `Xunit` or `appveyor` loggers, visit following repositories:
If you're looking for `NUnit`, `xUnit` or `appveyor` loggers, visit following repositories:

- <https://github.com/spekt/nunit.testlogger>
- <https://github.com/spekt/xunit.testlogger>
- <https://github.com/spekt/appveyor.testlogger>

## Usage

The JUnit Test Logger generates xml reports in the [Ant Junit Format](https://github.com/windyroad/JUnit-Schema), which the JUnit 5 repository refers to as the de-facto standard. While the generated xml complies with that schema, it does not contain values in every case. For example, the logger currently does not log any `properties`. Please [refer to a sample file](docs/assets/TestResults.xml) to see an example. If you find that the format is missing data required by your CI/CD system, please open an issue or PR.
The JUnit Test Logger generates xml reports in the [Jenkins JUnit Format](https://github.com/junit-team/junit5/blob/main/platform-tests/src/test/resources/jenkins-junit.xsd), which the JUnit 5 repository refers to as the de-facto standard. While the generated xml complies with that schema, it does not contain values in every case. For example, the logger currently does not log any `properties`. Please [refer to a sample file](docs/assets/TestResults.xml) to see an example. If you find that the format is missing data required by your CI/CD system, please open an issue or PR.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets make sure to update the sample file once all other changes are complete.


To use the logger, follow these steps:

Expand All @@ -47,6 +47,89 @@ test logs from over-writing eachother.

[config-wiki]: https://github.com/spekt/testlogger/wiki/Logger-Configuration

### Junit Output Notes
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: JUnit


The xml reports will have standard output (stdout) and standard error (stderr) from the test output. Stdout is logged on a per-testcase basis, but stderr
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section looks a bit incomplete. The beginning trails off and parts of mstest seems missing. I think the examples are helpful, but lets put them in another file for just the standard and error out.

Since you are pointing out that failed tests behave differently can we just show all the options. Just 2 test methods should be fine.
*Passing Test
* std out
* error out
*Failing Test
* std out
* error out

I think it would be fine to have the docs show more complete examples in a details tag if its getting too long.

In this file, lets have a short section after ### Customizing Junit XML Contents which points out that this stuff varies per testing framework, and links to all the details.


#### NUnit

On a testcase failure, NUnit will redirect stderr to stdout for each `TestCase`. Because of this, both stdout and stderr will be captured in the JUnit xml file within the `<system-out></system-out>` tags for each test case. It will also be capture within the `<system-out></system-out>` tags at the `<testsuite>` level.

##### NUnit Example
```csharp
[TestCase]
public void TestThatFailsWithStdErr()
{
Console.WriteLine("Output to stderr", Console.Error);
Assert.Fail();
}
```
Console Output
```
Failed TestThatFailsWithStdErr() [71 ms]
Stack Trace:
at NUnit.UnitTests.Tests.TestThatFailsWithStdErr() in /home/runner/work/dotnet_test_output_reporting/dotnet_test_output_reporting/NUnit.UnitTests/UnitTest1.cs:line 42

Standard Output Messages:
Output to stderr
```

```xml
<testcase classname="NUnit.UnitTests.Tests" name="TestThatFailsWithStdOutAndStdErr()" time="0.0004670">
<failure type="failure">at NUnit.UnitTests.Tests.TestThatFailsWithStdOutAndStdErr() in D:\Developer\dotnet_test_output_reporting\NUnit.UnitTests\UnitTest1.cs:line 50</failure>
<system-out>Output to stdout
Output to stderr

</system-out>
</testcase>
```

#### xUnit

On a testcase failure, by default xUnit will not log any stdout or stderr to the console. Stdout from each `Fact` will be captured in the JUnit xml within the `<system-out></system-out>` tags at the `testsuite` level.

##### xUnit Example
```csharp
[Fact]
public void TestThatFailsWithStdErr()
{
Console.WriteLine("Output to stderr", Console.Error);
Assert.True(false, "test failure");
}
```
```
Expected: True
Actual: False
Stack Trace:
at XUnit.UnitTests.UnitTest1.TestThatFailsWithStdErr() in /home/runner/work/dotnet_test_output_reporting/dotnet_test_output_reporting/XUnit.UnitTests/UnitTest1.cs:line 41
Failed XUnit.UnitTests.UnitTest1.TestThatFailsWithStdOutAndStdErr [< 1 ms]
Error Message:
test failure
```
```xml
<testcase classname="XUnit.UnitTests.UnitTest1" name="TestThatFailsWithStdErr" time="0.0030326">
<failure type="failure" message="test failure&#xD;&#xA;Expected: True&#xD;&#xA;Actual: False">at XUnit.UnitTests.UnitTest1.TestThatFailsWithStdErr() in D:\Developer\dotnet_test_output_reporting\XUnit.UnitTests\UnitTest1.cs:line 41</failure>
</testcase>
```


#### MsTest

#### MsTest Example
```csharp

```
```
Failed TestThatFailsWithStdErr [< 1 ms]
Error Message:
Assert.Fail failed.
Stack Trace:
at MsTest.UnitTests.UnitTest1.TestThatFailsWithStdErr() in /home/runner/work/dotnet_test_output_reporting/dotnet_test_output_reporting/MsTest.UnitTests/UnitTest1.cs:line 42

Standard Output Messages:
Output to stderr
```

### Customizing Junit XML Contents

There are several options to customize how the junit xml is populated. These options exist to
Expand Down
28 changes: 28 additions & 0 deletions src/JUnit.Xml.TestLogger/JunitXmlSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,34 @@ private XElement CreateTestCaseElement(TestResultInfo result)
testcaseElement.Add(skippedElement);
}

StringBuilder stdErr = new StringBuilder();
foreach (var m in result.Messages)
{
if (TestResultMessage.StandardErrorCategory.Equals(m.Category, StringComparison.OrdinalIgnoreCase))
{
stdErr.AppendLine(m.Text);
}
}

if (!string.IsNullOrWhiteSpace(stdErr.ToString()))
{
testcaseElement.Add(new XElement("system-err", stdErr.ToString()));
}

StringBuilder stdOut = new StringBuilder();
foreach (var m in result.Messages)
{
if (TestResultMessage.StandardOutCategory.Equals(m.Category, StringComparison.OrdinalIgnoreCase))
{
stdOut.AppendLine(m.Text);
}
}

if (!string.IsNullOrWhiteSpace(stdOut.ToString()))
{
testcaseElement.Add(new XElement("system-out", stdOut.ToString()));
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also didn't wrap this with XCData like the NUnit one because I wanted to match the existing system-out/system-err printing. I don't know enough about JUnit to know if parsers should expect CDATA or not.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also something we could do a quick test on. But if the logged text has any xml characters that need to be escaped, I expect thats what will happen here. If we wrap it in CDATA, the characters won't be escaped and should therefore be more readable.

https://en.wikipedia.org/wiki/CDATA

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Siphonophora would you like me to wrap the per-testcase output in CDATA and the per-testsuite output? I'd consider this to be a breaking change because it's not currently wrapped on line 229.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I would call it a breaking change so long as we are still passing the xml validation. But, we can break this out to its own issue and decide on it later.

}

return testcaseElement;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,7 @@ public void TestResultFileShouldContainTestCases()
.ToList();

Assert.AreEqual(14, failures.Count());
Assert.IsTrue(failures.All(x => x.Descendants().Count() == 1));
Assert.IsTrue(failures.All(x => x.Descendants().First().Attribute("type").Value == "failure"));
Assert.IsTrue(failures.All(x => x.Descendants().Any(element => element.Attribute("type").Value == "failure")));

// Check failures
var skips = testcases
Expand All @@ -96,6 +95,19 @@ public void TestResultFileShouldContainTestCases()
Assert.AreEqual(6, skips.Count());
}

[TestMethod]
public void TestCasesShouldContainStandardOut()
{
var node = this.resultsXml.XPathSelectElements("/testsuites/testsuite").Descendants();
var testcases = node.Where(x => x.Name.LocalName == "testcase").ToList();

var testCasesWithSystemOut = testcases
.Where(x => x.Descendants().Any(y => y.Name.LocalName == "system-out"))
.ToList();

Assert.AreEqual(2, testCasesWithSystemOut.Count());
}

[TestMethod]
public void TestResultFileShouldContainStandardOut()
{
Expand All @@ -108,7 +120,7 @@ public void TestResultFileShouldContainStandardOut()
}

[TestMethod]
public void TestResultFileShouldContainErrordOut()
public void TestResultFileShouldContainErrorOut()
{
var node = this.resultsXml.XPathSelectElement("/testsuites/testsuite/system-err");

Expand Down