Parallelization & Advanced Patterns
Parallel Execution
// AssemblyInfo.cs or any file in the test project
[assembly: Parallelizable(ParallelScope.Fixtures)]
[assembly: LevelOfParallelism(4)]
// Combinatorial explosion vs Pairwise coverage
[Test]
public void Render_AcrossBrowsersAndResolutions(
[Values("Chrome", "Firefox", "Safari")] string browser,
[Values(720, 1080, 1440)] int height)
{
// [Combinatorial] (default) = 3 * 3 = 9 runs
// [Pairwise] on the method instead = fewer runs, still covers each pair once
}
// Shared mutable state is a hazard once fixtures run in parallel —
// scope resources per-instance, not static, when [Parallelizable] is on.
[OneTimeSetUp]
public void StartTestServer()
{
_server = new InMemoryTestServer(); // instance field, not static
}TestContext & Diagnostics
[TearDown]
public void SaveScreenshotOnFailure()
{
var outcome = TestContext.CurrentContext.Result.Outcome;
if (outcome.Status == TestStatus.Failed)
{
var path = Path.Combine(TestContext.CurrentContext.WorkDirectory, "failure.png");
_driver.TakeScreenshot(path);
TestContext.WriteLine($"Saved failure screenshot to {path}");
}
}
// Retry vs Repeat — very different intent
[Retry(3)] // keeps the run a pass if ANY attempt succeeds — use sparingly, can mask flakiness
public void FlakyNetworkCall() { ... }
[Repeat(5)] // requires EVERY repetition to pass — good for surfacing intermittent bugs
public void RaceConditionCandidate() { ... }
[Timeout(2000)] // fails if the test doesn't finish in time; runs on a separate thread
public void MustCompleteQuickly() { ... }Setup Fixtures & Custom Constraints
// Runs once for every fixture in this namespace — good for expensive shared
// infrastructure like a test container, above per-fixture [OneTimeSetUp].
namespace MyApp.Tests.Integration
{
[SetUpFixture]
public class NamespaceSetup
{
[OneTimeSetUp]
public void StartContainer() => TestDatabase.Start();
[OneTimeTearDown]
public void StopContainer() => TestDatabase.Stop();
}
}
// Custom, reusable constraint
public class ValidEmailConstraint : Constraint
{
public override ConstraintResult ApplyTo<TActual>(TActual actual)
{
bool isValid = actual is string s && s.Contains('@');
return new ConstraintResult(this, actual, isValid);
}
}
public static class Is2
{
public static ValidEmailConstraint ValidEmail() => new ValidEmailConstraint();
}
// Assert.That(user.Email, Is2.ValidEmail());Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free