-
Notifications
You must be signed in to change notification settings - Fork 106
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
Console output handling reimagined #1167
Comments
Pull request for nunit3-vs-adapter.issues in preparation... ⏳ |
Thanks @ravigneaux-1 ! Awesome work. What you have written above should also go into the docs, after the issues have been cleared out. |
Pull request for nunit3-vs-adapter.issues created 📦 It contains demonstrators for the issues described (so that everyone can try it out easily) and a very crude proof of concept for the mentioned I didn't live up to the expectations I fueled - but only in the sense that the code examples above are not 100% accurate. I thought I could adapt my original implementation on-the-fly, but I didn't manage to do so. You actually get more: The |
Maybe the
Just some things that came to my mind... |
Console output is vital for the verification of the system under test (SUT) and understanding the status of the test execution. However, the current approach in the NUnit test adapter has some limitations. This issue suggests changes to the handling of console output from both the system under test and the test implementation, initiating a discussion for potential improvements.
Introduction
This discussion focuses primarily on the interaction between the test adapter and the test platform. We'll also have a brief look at the implications the adapter's capabilities have on test loggers.
Basically, there are two use cases for console output that we need to differentiate:
In my point of view, the current implementation of the NUnit test adapter assumes that console output is primarily used for logging information, warnings, and errors during test execution, which aligns with use case 1. However, this discussion addresses enabling use case 2.
Example use case
Under which circumstances would the system under test typically write to the console, and why is this relevant to the tests in the first place?
Let's assume we develop a console application. It's perfectly valid for such an application to write to the standard output stream as well as the standard error stream.1 For example, a simple console application could look something like this:
Unit tests for such an application could include test cases like the following:
Main
method returns zero exit code if called with single argumentMain
method returns non-zero exit code if called with no argumentsMain
method returns zero exit code if called with multiple argumentsAn implementation of these test cases using the NUnit framework could look like the following:
Test case implementation in the NUnit framework
For comparison, let's implement the same tests using MSTest and the xUnit framework as well:
Test case implementation in MSTest
Test case implementation in the xUnit framework
Now we'll examine how the console output interacts with the test platform and impacts the test execution...
Test run result in the Test Explorer
First, let's run the tests in the Visual Studio Test Explorer.
❌ After the test run has finished, the Test Explorer issues warnings.
The developer would read this as an indication for issues in the tests ("The test run has finished with warnings, something must be wrong."), when in reality that's not the case at all.
Root cause
The NUnit test adapter forwards console output from the test host to the test platform via the
IMessageLogger
. Text written to the standard output stream is forwarded as informational or warning messages, or not at all, according to adapter settingConsoleOut
. Text written to the standard error stream is always forwarded as warning messages, regardless of the adapter settings.Proposed change
It should be possible to control the message level (information, warning, error) with which console output from the test host is forwarded, and to disable forwarding altogether, both for the standard output and for the standard error stream. The adapter setting
ConsoleOut
should control both standard streams, or a new adapter settingConsoleError
should be introduced to control the standard error stream independently.Rationale
Writing to the console is not inherently erroneous behavior of the system under test. Rather, it's often expected behavior that itself is testable. While writing to the console within the test implementation, on the other hand, might be used for logging, it should be noted that there are better alternatives for this purpose.2 Extending the adapter settings as proposed enables users to implement both use cases: Either suppressing false-positive warnings for "normal" console output from the system under test or continue writing to the console for logging purposes.
The adapters of MSTest and the xUnit framework do not issue warnings for console output either.
Test run result in the Test Explorer using MSTest
✅ No warnings are issued.
Test run result in the Test Explorer using the xUnit framework
✅ No warnings issued.
Standard output and standard error in the Test Explorer
Another feature of the Test Explorer is listing text written to the standard output and standard error streams from the test host individually for each test case. The NUnit test adapter correctly implements this feature:3
✅ Console output from application listed for each test case.
In this regard, NUnit keeps up with MSTest and even outperforms the xUnit framework, which appears to ignore console output from the test host.
Standard streams in the Test Explorer using MSTest
✅ Console output from application listed for each test case.
Missing standard streams in the Test Explorer using the xUnit framework
❌ Console output from application not listed for each test case.
Proposed change
None. This feature should be maintained as it is, even if the test adapter's handling of console output is changed otherwise.
Rationale
The feature enables developers to inspect console output from the test host on a per test case basis and makes the output available to test loggers.
Console output in the Tests Output Pane
Another issue of running the tests in Visual Studio with the NUnit test adapter concerns the output generated during the test run:
❌ Console output from the system under test is listed in the Tests Output Pane.
This clutters the output with text that cannot be attributed to individual test cases4 and distracts from the actual diagnostic messages of the test run.
Root cause
The NUnit test adapter forwards console output from the test host to the test platform via the
IMessageLogger
, according to theConsoleOut
setting. In addition, theVerbosity
setting acts like a filter in the message forwarding process.5 The forwarding of informational messages can be disabled, but warning and error messages are forwarded even with the lowest verbosity level.Proposed change
It should be possible to disable forwarding of console output from the test host, so that it does not appear in the test output. Adapter setting
Verbosity
should not control console output from the test host, since the latter is already controlled by theConsoleOut
(and the proposedConsoleError
) setting. Instead, the verbosity level should only control diagnostics from the test framework and/or test adapter itself.Rationale
The console output from the system under test does not provide useful information about test execution. With large numbers of test cases, isolating relevant information from the output can become a real problem.
Neither MSTest nor the xUnit framework write the standard streams to the test output, and it does not appear they would offer adapter settings to enable such functionality.
Output of the test run using MSTest
✅ Console output from the application is not listed in the Tests Output Pane.
The available adapter settings do not include a related setting.
Output of the test run using the xUnit framework
✅ Console output from the application is not listed in the Tests Output Pane.
The available adapter settings do not include a related setting.
Console output in the console logger
Next, let's run the test using
dotnet test
and the default console logger.❌ Console output from the system under test appears on the console.
The line of argument follows that of the previous section. While I acknowledge that the test loggers' behavior is not within control of NUnit, the example shows the implications of the test adapter's handling of console output. The proposed changes to the adapter settings would provide more control over which messages the test loggers will receive or not.6
The default console logger does not write the application output when using MSTest or the xUnit framework.
Output of the
dotnet test
run using MSTest✅ Console output from the system under test does not appear on the console.
Output of the
dotnet test
run using the xUnit framework✅ Console output from the system under test does not appear on the console.
Capturing console output
Console output from the test host, including the system under test as well as the test adapter itself, will go nowhere by default, since its standard streams are not connected to the standard streams of the test platform. The adapter or framework must intercept/redirect the standard streams of the test host and capture the console output.7
This got me thinking: If the output needs to be captured anyway, why not make it available to the tests as well? After all, tests might want to verify that specific outputs are produced by the system under test.
That's why I've attempted to implement a
ConsoleRecorder
class. It allows users to selectively record console output and read it back in the test cases:Implementing this feature in NUnit would be highly beneficial. The correct domain, however, whether within the adapter or the framework itself, is open to discussion. Also, the design should be reviewed with care.
Summary
The proposed changes aim to refine the handling of console output within the test adapter, giving the user more control over how console output should be interpreted - as data from the system under test or as messages logged in the tests. At the same time, the captured console output is provided to the tests for verification.
The graphical overview below illustrates the modifications, where blocks with question marks resemble parts of the existing adapter implementation:
Footnotes
For instance, the POSIX standard defines
In practice, diagnostic output includes informational messages (e.g., progress updates), as well as warnings (e.g., about corrections applied to the input) and actual errors (e.g., failed operations), while conventional output includes "productive" data (i.e., the result of the executed command) that is meaningful to the user and could potentially be used as input for subsequent commands. ↩
I intend to create another issue for discussion of logging mechanisms. ↩
This mechanism involves adding a
TestResultMessage
with the corresponding category for each output to theTestResult
. I believe this is currently implemented in theTestConverter
of the test adapter. ↩Adapter setting
UseTestNameInConsoleOutput
appears to be working only for the standard output stream, but not for the standard error stream. ↩This is currently implemented in the
TestLogger
of the test adapter, from what I can tell. ↩It should be noted that console output can be made available to test loggers via the
TestResult.Messages
, even if forwarding of log messages (information, warnings, errors) is disabled. ↩I believe this is currently implemented in the
NUnitTestAssemblyRunner
of the framework. ↩The text was updated successfully, but these errors were encountered: