Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/articles/nunit/extending-nunit/Execution-Hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,16 @@ Each hook receives a `HookData` instance:

Use these fields for logging, conditional logic, or adaptive cleanup.

## One-Time vs Per-Test Setup/TearDown

`BeforeEverySetUpHook`/`AfterEverySetUpHook` and `BeforeEveryTearDownHook`/`AfterEveryTearDownHook` run for both per-test and one-time setup/teardown. Inside a hook, the supported way to distinguish the two is the current test context: `Context.Test.IsSuite` is `true` for [OneTimeSetUp]/[OneTimeTearDown] (suite context) and `false` for [SetUp]/[TearDown] (test method context).

See [Example: One-Time vs Per-Test Setup and TearDown](#example-one-time-vs-per-test-setup-and-teardown) for a complete hook implementation.

## Example: One-Time vs Per-Test Setup and TearDown

[!code-csharp[ExecutionHookAttributeExample](~/snippets/Snippets.NUnit/ExecutionHookExamples.cs#OneTimeVsPerTestSetupTearDownExample)]

## Example: Measure Time for Setup

[!code-csharp[ExecutionHookAttributeExample](~/snippets/Snippets.NUnit/ExecutionHookExamples.cs#TimingHookAttribute)]
Expand Down
22 changes: 20 additions & 2 deletions docs/snippets/Snippets.NUnit/ExecutionHookExamples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,32 @@ public override void AfterEverySetUpHook(HookData hookData)
if (_starts.TryGetValue(key, out var start))
{
var elapsed = DateTime.UtcNow - start;
TestContext.WriteLine($"[Timing] " +
TestContext.Progress.WriteLine($"[Timing] " +
$"{hookData.Context.Test.MethodName} " +
$"took {elapsed.TotalMilliseconds:F1} ms");
}
}
}
#endregion

#region OneTimeVsPerTestSetupTearDownExample
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public sealed class OneTimeVsPerTestSetupTearDownHookAttribute : ExecutionHookAttribute
{
public override void BeforeEverySetUpHook(HookData data)
{
var scope = data.Context.Test.IsSuite ? "OneTimeSetUp" : "SetUp";
TestContext.Progress.WriteLine($"{scope}: {data.HookedMethod?.Name}");
}

public override void AfterEveryTearDownHook(HookData data)
{
var scope = data.Context.Test.IsSuite ? "OneTimeTearDown" : "TearDown";
TestContext.Progress.WriteLine($"{scope}: {data.HookedMethod?.Name}");
}
}
#endregion

#region Usage
[TestFixture]
[TimeMeasurementHook]
Expand All @@ -52,7 +70,7 @@ private void Log(string phase, HookData data, bool withException = false)
var name = data.Context.Test.MethodName;
var exInfo = withException && data.Exception != null ?
$" (EX: {data.Exception.GetType().Name})" : string.Empty;
TestContext.WriteLine($"[{phase}] {name}{exInfo}");
TestContext.Progress.WriteLine($"[{phase}] {name}{exInfo}");
}

public override void BeforeEverySetUpHook(HookData d)
Expand Down