Testing & Reliable Design · Lesson 19

Understand runner and console limits

Make portable checks self-report while recognizing what a dedicated test framework adds.

Define the observable contract

A portable console program can evaluate conditions, print failures, and return a nonzero exit code. Those are useful self-checking mechanics, but they do not make the program a full unit-test framework. MSTest, xUnit, and their runners supply genuine project integration, discovery, diagnostics, and execution features under documented configuration. This lesson keeps the boundary visible. Its small array of checks uses ordinary C# and can run inside SharpRune, while a separate real test project is needed when a developer wants the corresponding framework and command-line workflow on a development machine.

Walk through the real program

The first prepared condition compares two plus three with five. The second checks that a single space is considered whitespace. Both are true, so the program prints sum:PASS and blank:PASS, followed by failed:0. The top-level return then selects process exit code zero. No test attributes are discovered and no assertion package is called. The visible labels are produced by this loop itself. If a condition were false, the code would count it and choose exit code one; a real invoking host would need to observe that outcome rather than merely display an old successful output.

Why the design is testable

The distinction between printing and asserting matters for automation. A line saying the expected number is five does not compare the actual result with five. This sample performs comparisons before reporting, then exposes a failure status to its caller. A proper runner can additionally identify individual discovered cases, preserve exception context, configure fixtures, and integrate results with build tools. Those features are not reproduced by renaming the loop RunTests. Use the lightweight form for portable learning and small explicit checks while choosing the real tool when a production project's verification needs its capabilities.

A boundary that matters

Running dotnet test on an ordinary console project is not the same as running this program through dotnet run. A genuine test project needs its supported SDK, framework packages or equivalent documented configuration, and host integration. Tooling details vary with runner and version, so follow the selected framework's current instructions rather than mixing attributes from one system with another system's project setup. The browser runtime does not install a development CLI or secretly invoke a desktop test runner just because the lesson uses words such as check, expected, or PASS.

Practice: isolate one behavior

First change the sum expectation from five to six as a deliberate mismatch. Predict one FAIL line and failed:1, with exit code one in a normal standalone console execution. Hint: inspect the comparison and the return path, not only the printed label. Restore the expectation afterward. Then add a third independently justified passing condition and update the expected report. Keep the sample small enough that a reader can verify its own mechanics without trusting an elaborate home-grown framework.

Practice: challenge another assumption

For a second exercise, create a separate genuine MSTest or xUnit project on a development machine using its documented template and dependencies. Move a small domain function into a library and express its behavior using that framework's actual assertions. Hint: keep this console lesson distinct from the test project; do not paste unsupported namespaces here and assume a runner appears. Compare how the real runner reports a mismatch, an unexpected exception, and an asynchronous test, noting which capabilities the portable loop intentionally does not provide.

Review the evidence

The original report is self-checking within its explicitly limited design. It returns success only because both comparisons passed, not because writing PASS has magical meaning. A reliable testing workflow makes that causal relationship and its execution host clear. Choose tools according to the project's needs, preserve independent expectations, and ensure automation observes failures. A small transparent console demonstration can teach those principles honestly without pretending to replace a mature framework or claiming that a successful browser run already executed a separate desktop test suite.

  • The baseline performs two real comparisons and returns exit code zero.
  • Printed text by itself does not establish an assertion.
  • A proper dotnet test workflow requires the genuine test project and runner.

A working example

using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

var checks = new[] { (Name: "sum", Passed: 2 + 3 == 5), (Name: "blank", Passed: string.IsNullOrWhiteSpace(" ")) };
int failed = 0;
foreach (var check in checks)
{
    Console.WriteLine($"{check.Name}:{(check.Passed ? "PASS" : "FAIL")}");
    if (!check.Passed) failed++;
}
Console.WriteLine($"failed:{failed}");
return failed == 0 ? 0 : 1;

Expected output

sum:PASS
blank:PASS
failed:0
Try this
This is a console check program; dotnet test needs a real test project, framework and host configuration.

Further reading