LyraLearn AI Learning Platform
Exams
← Module 8 Β· Testing .NET Applications
🎧 Listen

Test Data and CI

A test suite earns its keep in two ways: developers trust it (it fails only when something is really broken), and it runs automatically where merges happen. This lesson covers both β€” the craft of test data, and wiring the suite into Azure DevOps.

Builders and fixtures: taming test data

Domain objects in this system are deep: a candidate has transcripts, transcripts have courses, courses map to SMR domains. Constructing that inline makes every test forty lines of noise β€” and when a required field is added, a hundred tests break at once. The builder pattern fixes both:

var candidate = new CandidateBuilder()
    .WithCourse("MATH 210", units: 4.5m)
    .WithExamScore("CSET Math II", passed: true)
    .Build();

The builder supplies valid defaults for everything, and each test overrides only what it's actually about β€” so the test reads as its own specification, and schema changes are absorbed in one place. Fixtures complement builders for shared expensive setup: xUnit's IClassFixture<T> (or NUnit's [OneTimeSetUp]) builds a database or seeded context once per class instead of once per test. Use fixtures for infrastructure, builders for data β€” and keep fixture state read-only, or tests start coupling through it.

Deterministic tests, or nothing

A flaky test β€” passes locally, fails in the pipeline, passes on re-run β€” is worse than no test: the team learns to click "re-run" and real failures die in the noise. Flakiness has a short list of causes, all avoidable:

House rule worth adopting: a flaky test gets fixed or deleted the week it's detected β€” never quarantined indefinitely.

Running in Azure DevOps

In an Azure DevOps pipeline, tests are a YAML step away β€” dotnet test (or the VSTest@2 task for Framework projects) with --logger trx so results publish to the run summary, giving reviewers pass/fail history and failure details per build. The policies that make it matter: run tests on every pull request via a branch policy (a red suite blocks the merge), keep the whole job fast enough that nobody is tempted to skip it, and treat a broken main-branch build as the team's top priority.

Coverage: a signal, not a target

--collect "XPlat Code Coverage" adds a coverage report to the pipeline. Read it as a flashlight: uncovered evaluation-rule code is a genuine gap worth closing. Never make the number a goal β€” chasing "80%" produces assertion-free tests over trivial code (Goodhart's law), while your riskiest branch sits uncovered. Coverage tells you where you haven't tested; only test quality tells you whether you've tested well.

🧠 Quiz yourself on this lesson →

Ask the AI Tutor

Grounded in the course lessons β€” it cites its sources and says when it doesn't know.