Skip to content

Inject IConsole into ValidatorRunner for testable console output#75

Merged
304NotModified merged 4 commits intomainfrom
copilot/inject-iconsole-into-validaterrunner
Apr 17, 2026
Merged

Inject IConsole into ValidatorRunner for testable console output#75
304NotModified merged 4 commits intomainfrom
copilot/inject-iconsole-into-validaterrunner

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 17, 2026

ValidatorRunner wrote directly to Console.WriteLine/Console.Error, making stdout/stderr output untestable. Since System.CommandLine 2.0.6 removed IConsole, the abstraction is defined in-project.

Core changes

  • IConsole — single interface with async WriteLineAsync(string) and WriteErrorLineAsync(string) methods
  • SystemConsole — production impl delegating to Console.Out.WriteLineAsync / Console.Error.WriteLineAsync
  • ValidatorRunnerIConsole console added as primary-constructor parameter; all three Console.* calls replaced:
    // before
    await Console.Error.WriteLineAsync($"No .slnx files found for input: {options.Input}");
    Console.WriteLine($"SonarQube report written to: ...");
    
    // after
    await console.WriteErrorLineAsync($"No .slnx files found for input: {options.Input}");
    await console.WriteLineAsync($"SonarQube report written to: ...");
  • Program.csIConsole registered as new SystemConsole() singleton in DI

Test infrastructure

  • FakeConsole — in-memory impl with List<string> OutputLines and List<string> ErrorLines properties for easy assertion
  • MockFileSystemGetFileSize returns 0; no stream-wrapping complexity
  • ValidatorRunnerTests_console is a shared instance field (fresh per test via NUnit's per-test instantiation); factory helpers are instance methods that use _console; three new tests cover the "no files found" error path and the SonarQube/SARIF confirmation messages
Original prompt

Goal

Make ValidatorRunner testable by replacing direct Console.WriteLine / Console.Error.WriteLineAsync calls with an injected System.CommandLine.IConsole abstraction. Add the missing tests that verify console output. Update all existing test helpers that construct ValidatorRunner so they compile and pass.


1. Change ValidatorRunner – inject IConsole

File: src/SLNX-validator/ValidatorRunner.cs

Add IConsole console as a primary-constructor parameter (after IFileSystem fileSystem).

Replace:

await Console.Error.WriteLineAsync($"No .slnx files found for input: {options.Input}");

with:

console.Error.Write($"No .slnx files found for input: {options.Input}{Environment.NewLine}");

Replace both:

Console.WriteLine($"SonarQube report written to: {options.SonarqubeReportPath} ({size} bytes)");

and:

Console.WriteLine($"SARIF report written to: {options.SarifReportPath} ({size} bytes)");

with:

console.Out.Write($"SonarQube report written to: {options.SonarqubeReportPath} ({size} bytes){Environment.NewLine}");

and:

console.Out.Write($"SARIF report written to: {options.SarifReportPath} ({size} bytes){Environment.NewLine}");

Add the using: using System.CommandLine;


2. Register IConsole in DI – Program.cs

File: src/SLNX-validator/Program.cs

In the ServiceCollection setup, add:

.AddSingleton<IConsole>(new SystemConsole())

SystemConsole is the concrete type in System.CommandLine that wraps the real Console.


3. Update ValidatorRunnerTests.cs

File: tests/SLNX-validator.Tests/ValidatorRunnerTests.cs

3a. Add helper to create a test console

At the top of the class add a private helper that creates a simple in-memory console for testing. System.CommandLine ships a TestConsole class; use that.

Add using: using System.CommandLine;

3b. Update CreateRunner and CreateRunnerWithSlnx

Both factory methods must accept an optional IConsole? console = null parameter and pass console ?? new TestConsole() as the last argument to the ValidatorRunner constructor.

3c. Add missing tests for console output

Add a new test region #region RunAsync – console output with the following tests (no more than needed):

[Test]
public async Task RunAsync_NoFilesFound_WritesErrorToConsole()
{
    // Arrange
    var console = new TestConsole();
    var runner = CreateRunner(new MockFileSystem(), console: console);

    // Act
    await runner.RunAsync(Options("nonexistent.slnx"), CancellationToken.None);

    // Assert
    console.Error.ToString().Should().Contain("No .slnx files found for input: nonexistent.slnx");
}

[Test]
public async Task RunAsync_SonarqubeReportPath_WritesConfirmationToConsole()
{
    // Arrange
    var slnxPath = Path.GetFullPath("test.slnx");
    var console = new TestConsole();
    var runner = CreateRunnerWithSlnx(slnxPath, "<Solution />", console: console);
    var options = new ValidatorRunnerOptions(slnxPath, SonarqubeReportPath: "report.xml",
        ContinueOnError: false, RequiredFilesPattern: null, WorkingDirectory: ".");

    // Act
    await runner.RunAsync(options, CancellationToken.None);

    // Assert
    console.Out.ToString().Should().Contain("SonarQube report written to: report.xml");
}

[Test]
public async Task RunAsync_SarifReportPath_WritesConfirmationToConsole()
{
    // Arrange
    var slnxPath = Path.GetFullPath("test.slnx");
    var console = new TestConsole();
    var runner = CreateRunnerWithSlnx(slnxPath, "<Solution />", console: console);
    var options = new ValidatorRunnerOptions(slnxPath, SonarqubeReportPath: null,
        ContinueOnError: false, RequiredFilesPattern: null, WorkingDirectory: ".",
        SarifReportPath: "report.sarif");

    // Act
    await runner.RunAsync(options, CancellationToken.None);

    // Assert
    console.Out.ToString().Should().Contain("SARIF report written to: report.sarif");
}

Important: TestConsole from System.CommandLine has .Error and .Out as IStandardStreamWriter. Use .ToString() on them to get the written text.


Notes

  • Do not add new NuGet packages. System.CommandLine is already referenced in src/SLNX-validator/SLNX-validator.csproj and the test project references the main project, so TestConsole is already available in tests.
  • Keep all existing tests passing — only the two factory method signatures change (adding optional IConsole? console parameter).
  • Do not write more tests than listed above.

The following is the prior conversation context from the user's chat exploration (may be truncated):

User: We missen wat tests voor @304NotModified/SLNX-validator/files/src/SLNX-validator/ValidatorRunner.cs. maar we missen ook een abstractie voor de console. Is daar een goede package voor of kunnen we beter een eigen schrijven?

Assistant: Goede bevindingen! Het project gebruikt al System.CommandLine (...

This pull request was created from Copilot chat.

Copilot AI changed the title [WIP] Inject IConsole into ValidatorRunner for improved testability Inject IConsole into ValidatorRunner for testable console output Apr 17, 2026
Copilot AI requested a review from 304NotModified April 17, 2026 22:37
Comment thread src/SLNX-validator/SystemConsole.cs Outdated
Comment thread src/SLNX-validator/SystemConsole.cs
Comment thread tests/SLNX-validator.Tests/MockFileSystem.cs Outdated
Comment thread tests/SLNX-validator.Tests/TestConsole.cs Outdated
Comment thread tests/SLNX-validator.Tests/ValidatorRunnerTests.cs Outdated
…onsole required, remove SizeCapturingStream

Agent-Logs-Url: https://github.com/304NotModified/SLNX-validator/sessions/d2fc4b14-e446-4e41-8704-97bcd27ae058

Co-authored-by: 304NotModified <5808377+304NotModified@users.noreply.github.com>
Comment thread src/SLNX-validator/ValidatorRunner.cs Outdated
Comment thread tests/SLNX-validator.Tests/FakeConsole.cs Outdated
Comment thread tests/SLNX-validator.Tests/ValidatorRunnerTests.cs
…s, use instance _console field in tests

Agent-Logs-Url: https://github.com/304NotModified/SLNX-validator/sessions/72e734b1-9ad7-4cb3-91ee-5cddb2c4570e

Co-authored-by: 304NotModified <5808377+304NotModified@users.noreply.github.com>
@304NotModified 304NotModified marked this pull request as ready for review April 17, 2026 23:09
@304NotModified 304NotModified merged commit f3ec11c into main Apr 17, 2026
5 checks passed
@304NotModified 304NotModified deleted the copilot/inject-iconsole-into-validaterrunner branch April 17, 2026 23:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants