diff --git a/Directory.Packages.props b/Directory.Packages.props
index 82057a3..e04efde 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -11,7 +11,6 @@
-
diff --git a/TaskTurnstile.Testing/TaskStateManagerMoqExtensions.cs b/TaskTurnstile.Testing/TaskStateManagerMoqExtensions.cs
deleted file mode 100644
index 03ed4f9..0000000
--- a/TaskTurnstile.Testing/TaskStateManagerMoqExtensions.cs
+++ /dev/null
@@ -1,165 +0,0 @@
-using Moq;
-using Moq.Language.Flow;
-
-namespace TaskTurnstile.Testing;
-
-///
-/// Moq setup extensions for that eliminate boilerplate
-/// on the methods that accept a delegate.
-///
-///
-///
-/// The methods that take a Func<CancellationToken, Task> require verbose
-/// Returns<string, Func<...>, TimeSpan?, CancellationToken> calls.
-/// These extensions collapse that to a single line:
-///
-///
-/// // Before
-/// Mocker.GetMock<ITaskStateManager>()
-/// .Setup(m => m.TryRunAsync(
-/// It.IsAny<string>(),
-/// It.IsAny<Func<CancellationToken, Task>>(),
-/// It.IsAny<TimeSpan?>(),
-/// It.IsAny<CancellationToken>()))
-/// .Returns<string, Func<CancellationToken, Task>, TimeSpan?, CancellationToken>(
-/// async (_, work, _, ct) => { await work(ct); return true; });
-///
-/// // After
-/// Mocker.GetMock<ITaskStateManager>().SetupTryRunAsync(returns: true);
-///
-///
-/// Verify calls are unchanged — they already work naturally.
-///
-///
-public static class TaskStateManagerMoqExtensions
-{
- ///
- /// Sets up .
- /// When is true, the work delegate is invoked and the call returns true.
- /// When is false, work is skipped and the call returns false.
- ///
- /// The to configure.
- ///
- /// Whether the mock should run the work and return true, or skip and return false.
- ///
- ///
- /// The task name to match. When null (default), matches any task name.
- ///
- public static IReturnsResult SetupTryRunAsync(
- this Mock mock,
- bool returns,
- string? taskName = null)
- {
- var setup = taskName is null
- ? mock.Setup(m => m.TryRunAsync(
- It.IsAny(),
- It.IsAny>(),
- It.IsAny(),
- It.IsAny()))
- : mock.Setup(m => m.TryRunAsync(
- taskName,
- It.IsAny>(),
- It.IsAny(),
- It.IsAny()));
-
- if (returns)
- {
- return setup.Returns, TimeSpan?, CancellationToken>(
- async (_, work, _, ct) =>
- {
- await work(ct);
- return true;
- });
- }
-
- return setup.ReturnsAsync(false);
- }
-
- ///
- /// Sets up
- /// to run the work delegate and return wrapping .
- ///
- /// The to configure.
- /// The value to wrap in .
- ///
- /// The task name to match. When null (default), matches any task name.
- ///
- public static IReturnsResult SetupTryRunAsync(
- this Mock mock,
- T value,
- string? taskName = null)
- {
- var setup = taskName is null
- ? mock.Setup(m => m.TryRunAsync(
- It.IsAny(),
- It.IsAny>>(),
- It.IsAny(),
- It.IsAny()))
- : mock.Setup(m => m.TryRunAsync(
- taskName,
- It.IsAny>>(),
- It.IsAny(),
- It.IsAny()));
-
- return setup.Returns>, TimeSpan?, CancellationToken>(
- async (_, work, _, ct) =>
- {
- await work(ct);
- return TryRunResult.Ran(value);
- });
- }
-
- ///
- /// Sets up
- /// to skip work and return .
- ///
- /// The to configure.
- ///
- /// The task name to match. When null (default), matches any task name.
- ///
- public static IReturnsResult SetupTryRunAsyncToSkip(
- this Mock mock,
- string? taskName = null)
- {
- var setup = taskName is null
- ? mock.Setup(m => m.TryRunAsync(
- It.IsAny(),
- It.IsAny>>(),
- It.IsAny(),
- It.IsAny()))
- : mock.Setup(m => m.TryRunAsync(
- taskName,
- It.IsAny>>(),
- It.IsAny(),
- It.IsAny()));
-
- return setup.ReturnsAsync(TryRunResult.Skipped);
- }
-
- ///
- /// Sets up to invoke the work delegate.
- ///
- /// The to configure.
- ///
- /// The task name to match. When null (default), matches any task name.
- ///
- public static IReturnsResult SetupRunAsync(
- this Mock mock,
- string? taskName = null)
- {
- var setup = taskName is null
- ? mock.Setup(m => m.RunAsync(
- It.IsAny(),
- It.IsAny>(),
- It.IsAny(),
- It.IsAny()))
- : mock.Setup(m => m.RunAsync(
- taskName,
- It.IsAny>(),
- It.IsAny(),
- It.IsAny()));
-
- return setup.Returns, TimeSpan?, CancellationToken>(
- async (_, work, _, ct) => await work(ct));
- }
-}
diff --git a/TaskTurnstile.Testing/TaskStateManagerNSubstituteExtensions.cs b/TaskTurnstile.Testing/TaskStateManagerNSubstituteExtensions.cs
deleted file mode 100644
index 7171c84..0000000
--- a/TaskTurnstile.Testing/TaskStateManagerNSubstituteExtensions.cs
+++ /dev/null
@@ -1,157 +0,0 @@
-using NSubstitute;
-using NSubstitute.Core;
-
-namespace TaskTurnstile.Testing;
-
-///
-/// NSubstitute setup extensions for that eliminate boilerplate
-/// on the methods that accept a delegate.
-///
-///
-///
-/// // Before
-/// manager.TryRunAsync(
-/// Arg.Any<string>(),
-/// Arg.Any<Func<CancellationToken, Task>>(),
-/// Arg.Any<TimeSpan?>(),
-/// Arg.Any<CancellationToken>())
-/// .Returns(async ci =>
-/// {
-/// await ci.Arg<Func<CancellationToken, Task>>()(CancellationToken.None);
-/// return true;
-/// });
-///
-/// // After
-/// manager.SetupTryRunAsync(returns: true);
-///
-///
-public static class TaskStateManagerNSubstituteExtensions
-{
- ///
- /// Sets up .
- /// When is true, the work delegate is invoked and the call returns true.
- /// When is false, work is skipped and the call returns false.
- ///
- /// The NSubstitute substitute to configure.
- ///
- /// Whether the substitute should run the work and return true, or skip and return false.
- ///
- ///
- /// The task name to match. When null (default), matches any task name.
- ///
- public static ConfiguredCall SetupTryRunAsync(
- this ITaskStateManager manager,
- bool returns,
- string? taskName = null)
- {
- var configuredCall = taskName is null
- ? manager.TryRunAsync(
- Arg.Any(),
- Arg.Any>(),
- Arg.Any(),
- Arg.Any())
- : manager.TryRunAsync(
- taskName,
- Arg.Any>(),
- Arg.Any(),
- Arg.Any());
-
- if (returns)
- {
- return configuredCall.Returns(async (CallInfo ci) =>
- {
- await ci.Arg>()(ci.Arg());
- return true;
- });
- }
-
- return configuredCall.Returns(false);
- }
-
- ///
- /// Sets up
- /// to run the work delegate and return wrapping .
- ///
- /// The NSubstitute substitute to configure.
- /// The value to wrap in .
- ///
- /// The task name to match. When null (default), matches any task name.
- ///
- public static ConfiguredCall SetupTryRunAsync(
- this ITaskStateManager manager,
- T value,
- string? taskName = null)
- {
- var configuredCall = taskName is null
- ? manager.TryRunAsync(
- Arg.Any(),
- Arg.Any>>(),
- Arg.Any(),
- Arg.Any())
- : manager.TryRunAsync(
- taskName,
- Arg.Any>>(),
- Arg.Any(),
- Arg.Any());
-
- return configuredCall.Returns(async (CallInfo ci) =>
- {
- await ci.Arg>>()(ci.Arg());
- return TryRunResult.Ran(value);
- });
- }
-
- ///
- /// Sets up
- /// to skip work and return .
- ///
- /// The NSubstitute substitute to configure.
- ///
- /// The task name to match. When null (default), matches any task name.
- ///
- public static ConfiguredCall SetupTryRunAsyncToSkip(
- this ITaskStateManager manager,
- string? taskName = null)
- {
- var configuredCall = taskName is null
- ? manager.TryRunAsync(
- Arg.Any(),
- Arg.Any>>(),
- Arg.Any(),
- Arg.Any())
- : manager.TryRunAsync(
- taskName,
- Arg.Any>>(),
- Arg.Any(),
- Arg.Any());
-
- return configuredCall.Returns(TryRunResult.Skipped);
- }
-
- ///
- /// Sets up to invoke the work delegate.
- ///
- /// The NSubstitute substitute to configure.
- ///
- /// The task name to match. When null (default), matches any task name.
- ///
- public static ConfiguredCall SetupRunAsync(
- this ITaskStateManager manager,
- string? taskName = null)
- {
- var configuredCall = taskName is null
- ? manager.RunAsync(
- Arg.Any(),
- Arg.Any>(),
- Arg.Any(),
- Arg.Any())
- : manager.RunAsync(
- taskName,
- Arg.Any>(),
- Arg.Any(),
- Arg.Any());
-
- return configuredCall.Returns(async (CallInfo ci) =>
- await ci.Arg>()(ci.Arg()));
- }
-}
diff --git a/TaskTurnstile.Testing/TaskTurnstile.Testing.csproj b/TaskTurnstile.Testing/TaskTurnstile.Testing.csproj
index 2aa5c7c..cb30da9 100644
--- a/TaskTurnstile.Testing/TaskTurnstile.Testing.csproj
+++ b/TaskTurnstile.Testing/TaskTurnstile.Testing.csproj
@@ -3,11 +3,11 @@
TaskTurnstile.Testing
- Test helpers for TaskTurnstile. Provides FakeTaskStateManager (framework-agnostic in-memory
- test double) and setup extension methods for Moq and NSubstitute that eliminate boilerplate
- when mocking ITaskStateManager methods that accept a Func delegate.
+ Framework-agnostic test double for TaskTurnstile. Provides FakeTaskStateManager,
+ an in-memory implementation of ITaskStateManager that executes work inline — no
+ mocking framework required.
- Concurrency, Background Tasks, Named Lock, Testing, Fake, Test Double, Moq, NSubstitute
+ Concurrency, Background Tasks, Named Lock, Testing, Fake, Test Double
NugetREADME.md
@@ -17,8 +17,6 @@
-
-
diff --git a/TaskTurnstile.Tests/TaskTurnstile.Tests.csproj b/TaskTurnstile.Tests/TaskTurnstile.Tests.csproj
index cbb1da6..8695a09 100644
--- a/TaskTurnstile.Tests/TaskTurnstile.Tests.csproj
+++ b/TaskTurnstile.Tests/TaskTurnstile.Tests.csproj
@@ -10,7 +10,6 @@
-
diff --git a/TaskTurnstile.Tests/Testing/TaskStateManagerMoqExtensionsTests.cs b/TaskTurnstile.Tests/Testing/TaskStateManagerMoqExtensionsTests.cs
deleted file mode 100644
index 956f934..0000000
--- a/TaskTurnstile.Tests/Testing/TaskStateManagerMoqExtensionsTests.cs
+++ /dev/null
@@ -1,210 +0,0 @@
-using Moq;
-using TaskTurnstile.Testing;
-
-namespace TaskTurnstile.Tests.Testing;
-
-public class TaskStateManagerMoqExtensionsTests
-{
- // ── SetupTryRunAsync (bool) ───────────────────────────────────────────────
-
- [Fact]
- public async Task SetupTryRunAsync_ReturnsTrue_RunsWork()
- {
- var mock = new Mock();
- mock.SetupTryRunAsync(returns: true);
-
- var workRan = false;
- var result = await mock.Object.TryRunAsync("job",
- _ => { workRan = true; return Task.CompletedTask; },
- cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.True(result);
- Assert.True(workRan);
- }
-
- [Fact]
- public async Task SetupTryRunAsync_ReturnsFalse_SkipsWork()
- {
- var mock = new Mock();
- mock.SetupTryRunAsync(returns: false);
-
- var workRan = false;
- var result = await mock.Object.TryRunAsync("job",
- _ => { workRan = true; return Task.CompletedTask; },
- cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.False(result);
- Assert.False(workRan);
- }
-
- [Fact]
- public async Task SetupTryRunAsync_ReturnsTrue_PassesCancellationTokenToWork()
- {
- var mock = new Mock();
- mock.SetupTryRunAsync(returns: true);
-
- using var cts = new CancellationTokenSource();
- CancellationToken captured = default;
-
- await mock.Object.TryRunAsync("job",
- ct => { captured = ct; return Task.CompletedTask; },
- cancellationToken: cts.Token);
-
- Assert.Equal(cts.Token, captured);
- }
-
- [Fact]
- public async Task SetupTryRunAsync_WithTaskName_OnlyMatchesSpecifiedName()
- {
- var mock = new Mock();
- mock.SetupTryRunAsync(returns: true, taskName: "import-job");
-
- var workRan = false;
- await mock.Object.TryRunAsync("import-job",
- _ => { workRan = true; return Task.CompletedTask; },
- cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.True(workRan);
- }
-
- [Fact]
- public async Task SetupTryRunAsync_WithTaskName_DoesNotMatchOtherName()
- {
- var mock = new Mock();
- mock.SetupTryRunAsync(returns: true, taskName: "import-job");
-
- // No setup for "other-job" — returns default (false)
- var result = await mock.Object.TryRunAsync("other-job",
- _ => Task.CompletedTask,
- cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.False(result);
- }
-
- // ── SetupTryRunAsync ───────────────────────────────────────────────────
-
- [Fact]
- public async Task SetupTryRunAsync_Generic_RunsWorkAndReturnsRanWithValue()
- {
- var mock = new Mock();
- mock.SetupTryRunAsync(value: 42);
-
- var workRan = false;
- var result = await mock.Object.TryRunAsync("job",
- _ => { workRan = true; return Task.FromResult(0); },
- cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.True(result.Started);
- Assert.Equal(42, result.Value);
- Assert.True(workRan);
- }
-
- [Fact]
- public async Task SetupTryRunAsync_Generic_WithTaskName_OnlyMatchesSpecifiedName()
- {
- var mock = new Mock();
- mock.SetupTryRunAsync(value: 99, taskName: "my-job");
-
- var result = await mock.Object.TryRunAsync("my-job",
- _ => Task.FromResult(0),
- cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.True(result.Started);
- Assert.Equal(99, result.Value);
- }
-
- // ── SetupTryRunAsyncToSkip ─────────────────────────────────────────────
-
- [Fact]
- public async Task SetupTryRunAsyncToSkip_ReturnsSkipped_WorkNotRun()
- {
- var mock = new Mock();
- mock.SetupTryRunAsyncToSkip();
-
- var workRan = false;
- var result = await mock.Object.TryRunAsync("job",
- _ => { workRan = true; return Task.FromResult(0); },
- cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.Equal(TryRunResult.Skipped, result);
- Assert.False(workRan);
- }
-
- [Fact]
- public async Task SetupTryRunAsyncToSkip_WithTaskName_OnlyMatchesSpecifiedName()
- {
- var mock = new Mock();
- mock.SetupTryRunAsyncToSkip(taskName: "skip-job");
-
- var result = await mock.Object.TryRunAsync("skip-job",
- _ => Task.FromResult(0),
- cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.Equal(TryRunResult.Skipped, result);
- }
-
- // ── SetupRunAsync ─────────────────────────────────────────────────────────
-
- [Fact]
- public async Task SetupRunAsync_RunsWork()
- {
- var mock = new Mock();
- mock.SetupRunAsync();
-
- var workRan = false;
- await mock.Object.RunAsync("job",
- _ => { workRan = true; return Task.CompletedTask; },
- cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.True(workRan);
- }
-
- [Fact]
- public async Task SetupRunAsync_PassesCancellationTokenToWork()
- {
- var mock = new Mock();
- mock.SetupRunAsync();
-
- using var cts = new CancellationTokenSource();
- CancellationToken captured = default;
-
- await mock.Object.RunAsync("job",
- ct => { captured = ct; return Task.CompletedTask; },
- cancellationToken: cts.Token);
-
- Assert.Equal(cts.Token, captured);
- }
-
- [Fact]
- public async Task SetupRunAsync_WithTaskName_OnlyMatchesSpecifiedName()
- {
- var mock = new Mock();
- mock.SetupRunAsync(taskName: "export-job");
-
- var workRan = false;
- await mock.Object.RunAsync("export-job",
- _ => { workRan = true; return Task.CompletedTask; },
- cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.True(workRan);
- }
-
- // ── Verify still works after setup ───────────────────────────────────────
-
- [Fact]
- public async Task SetupTryRunAsync_VerifyByTaskName_Works()
- {
- var mock = new Mock();
- mock.SetupTryRunAsync(returns: true);
-
- await mock.Object.TryRunAsync("import-job",
- _ => Task.CompletedTask,
- cancellationToken: TestContext.Current.CancellationToken);
-
- mock.Verify(m => m.TryRunAsync(
- "import-job",
- It.IsAny>(),
- It.IsAny(),
- It.IsAny()), Times.Once);
- }
-}
diff --git a/TaskTurnstile.Tests/Testing/TaskStateManagerNSubstituteExtensionsTests.cs b/TaskTurnstile.Tests/Testing/TaskStateManagerNSubstituteExtensionsTests.cs
deleted file mode 100644
index a19e7cd..0000000
--- a/TaskTurnstile.Tests/Testing/TaskStateManagerNSubstituteExtensionsTests.cs
+++ /dev/null
@@ -1,210 +0,0 @@
-using NSubstitute;
-using TaskTurnstile.Testing;
-
-namespace TaskTurnstile.Tests.Testing;
-
-public class TaskStateManagerNSubstituteExtensionsTests
-{
- // ── SetupTryRunAsync (bool) ───────────────────────────────────────────────
-
- [Fact]
- public async Task SetupTryRunAsync_ReturnsTrue_RunsWork()
- {
- var sub = Substitute.For();
- sub.SetupTryRunAsync(returns: true);
-
- var workRan = false;
- var result = await sub.TryRunAsync("job",
- _ => { workRan = true; return Task.CompletedTask; },
- cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.True(result);
- Assert.True(workRan);
- }
-
- [Fact]
- public async Task SetupTryRunAsync_ReturnsFalse_SkipsWork()
- {
- var sub = Substitute.For();
- sub.SetupTryRunAsync(returns: false);
-
- var workRan = false;
- var result = await sub.TryRunAsync("job",
- _ => { workRan = true; return Task.CompletedTask; },
- cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.False(result);
- Assert.False(workRan);
- }
-
- [Fact]
- public async Task SetupTryRunAsync_ReturnsTrue_PassesCancellationTokenToWork()
- {
- var sub = Substitute.For();
- sub.SetupTryRunAsync(returns: true);
-
- using var cts = new CancellationTokenSource();
- CancellationToken captured = default;
-
- await sub.TryRunAsync("job",
- ct => { captured = ct; return Task.CompletedTask; },
- cancellationToken: cts.Token);
-
- Assert.Equal(cts.Token, captured);
- }
-
- [Fact]
- public async Task SetupTryRunAsync_WithTaskName_OnlyMatchesSpecifiedName()
- {
- var sub = Substitute.For();
- sub.SetupTryRunAsync(returns: true, taskName: "import-job");
-
- var workRan = false;
- await sub.TryRunAsync("import-job",
- _ => { workRan = true; return Task.CompletedTask; },
- cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.True(workRan);
- }
-
- [Fact]
- public async Task SetupTryRunAsync_WithTaskName_DoesNotMatchOtherName()
- {
- var sub = Substitute.For();
- sub.SetupTryRunAsync(returns: true, taskName: "import-job");
-
- // No setup for "other-job" — returns default (false)
- var result = await sub.TryRunAsync("other-job",
- _ => Task.CompletedTask,
- cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.False(result);
- }
-
- // ── SetupTryRunAsync ───────────────────────────────────────────────────
-
- [Fact]
- public async Task SetupTryRunAsync_Generic_RunsWorkAndReturnsRanWithValue()
- {
- var sub = Substitute.For();
- sub.SetupTryRunAsync(value: 42);
-
- var workRan = false;
- var result = await sub.TryRunAsync("job",
- _ => { workRan = true; return Task.FromResult(0); },
- cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.True(result.Started);
- Assert.Equal(42, result.Value);
- Assert.True(workRan);
- }
-
- [Fact]
- public async Task SetupTryRunAsync_Generic_WithTaskName_OnlyMatchesSpecifiedName()
- {
- var sub = Substitute.For();
- sub.SetupTryRunAsync(value: 99, taskName: "my-job");
-
- var result = await sub.TryRunAsync("my-job",
- _ => Task.FromResult(0),
- cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.True(result.Started);
- Assert.Equal(99, result.Value);
- }
-
- // ── SetupTryRunAsyncToSkip ─────────────────────────────────────────────
-
- [Fact]
- public async Task SetupTryRunAsyncToSkip_ReturnsSkipped_WorkNotRun()
- {
- var sub = Substitute.For();
- sub.SetupTryRunAsyncToSkip();
-
- var workRan = false;
- var result = await sub.TryRunAsync("job",
- _ => { workRan = true; return Task.FromResult(0); },
- cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.Equal(TryRunResult.Skipped, result);
- Assert.False(workRan);
- }
-
- [Fact]
- public async Task SetupTryRunAsyncToSkip_WithTaskName_OnlyMatchesSpecifiedName()
- {
- var sub = Substitute.For();
- sub.SetupTryRunAsyncToSkip(taskName: "skip-job");
-
- var result = await sub.TryRunAsync("skip-job",
- _ => Task.FromResult(0),
- cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.Equal(TryRunResult.Skipped, result);
- }
-
- // ── SetupRunAsync ─────────────────────────────────────────────────────────
-
- [Fact]
- public async Task SetupRunAsync_RunsWork()
- {
- var sub = Substitute.For();
- sub.SetupRunAsync();
-
- var workRan = false;
- await sub.RunAsync("job",
- _ => { workRan = true; return Task.CompletedTask; },
- cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.True(workRan);
- }
-
- [Fact]
- public async Task SetupRunAsync_PassesCancellationTokenToWork()
- {
- var sub = Substitute.For();
- sub.SetupRunAsync();
-
- using var cts = new CancellationTokenSource();
- CancellationToken captured = default;
-
- await sub.RunAsync("job",
- ct => { captured = ct; return Task.CompletedTask; },
- cancellationToken: cts.Token);
-
- Assert.Equal(cts.Token, captured);
- }
-
- [Fact]
- public async Task SetupRunAsync_WithTaskName_OnlyMatchesSpecifiedName()
- {
- var sub = Substitute.For();
- sub.SetupRunAsync(taskName: "export-job");
-
- var workRan = false;
- await sub.RunAsync("export-job",
- _ => { workRan = true; return Task.CompletedTask; },
- cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.True(workRan);
- }
-
- // ── Received() still works after setup ───────────────────────────────────
-
- [Fact]
- public async Task SetupTryRunAsync_ReceivedByTaskName_Works()
- {
- var sub = Substitute.For();
- sub.SetupTryRunAsync(returns: true);
-
- await sub.TryRunAsync("import-job",
- _ => Task.CompletedTask,
- cancellationToken: TestContext.Current.CancellationToken);
-
- await sub.Received(1).TryRunAsync(
- "import-job",
- Arg.Any>(),
- Arg.Any(),
- Arg.Any());
- }
-}
diff --git a/wiki/Testing.md b/wiki/Testing.md
index 5a5449c..d58c7c7 100644
--- a/wiki/Testing.md
+++ b/wiki/Testing.md
@@ -1,6 +1,6 @@
# Testing
-## Setup extension methods (recommended for Moq / NSubstitute)
+## FakeTaskStateManager (recommended)
Add [`TaskTurnstile.Testing`](https://www.nuget.org/packages/TaskTurnstile.Testing) to your test project:
@@ -8,76 +8,9 @@ Add [`TaskTurnstile.Testing`](https://www.nuget.org/packages/TaskTurnstile.Testi
dotnet add package TaskTurnstile.Testing
```
-The methods that take a `Func` delegate (`TryRunAsync`, `RunAsync`) require verbose generic type arguments when set up with Moq or NSubstitute. The Testing package provides one-liner extension methods that eliminate that boilerplate while leaving `Verify` / `Received` completely unchanged.
+`FakeTaskStateManager` is a framework-agnostic in-memory implementation of `ITaskStateManager`. It executes work inline with no distributed state, no polling, and no boilerplate.
-### Moq / AutoMocker
-
-```csharp
-using TaskTurnstile.Testing;
-
-// TryRunAsync — runs work and returns true
-Mocker.GetMock().SetupTryRunAsync(returns: true);
-
-// TryRunAsync — skips and returns false
-Mocker.GetMock().SetupTryRunAsync(returns: false);
-
-// RunAsync — runs work
-Mocker.GetMock().SetupRunAsync();
-
-// Generic TryRunAsync — runs work, returns Ran(value)
-Mocker.GetMock().SetupTryRunAsync(value: 42);
-
-// Generic TryRunAsync — skips
-Mocker.GetMock().SetupTryRunAsyncToSkip();
-```
-
-### NSubstitute
-
-```csharp
-using TaskTurnstile.Testing;
-
-var manager = Substitute.For();
-
-manager.SetupTryRunAsync(returns: true);
-manager.SetupTryRunAsync(returns: false);
-manager.SetupRunAsync();
-manager.SetupTryRunAsync(value: 42);
-manager.SetupTryRunAsyncToSkip();
-```
-
-### Matching a specific task name
-
-Pass `taskName` to make the setup match only that name. Any other name falls through to the Moq / NSubstitute default. This is useful when the thing you're testing is *which* name gets passed:
-
-```csharp
-// Moq
-Mocker.GetMock().SetupTryRunAsync(returns: true, taskName: "import-job");
-
-// Verify the correct name was used
-Mocker.GetMock()
- .Verify(m => m.TryRunAsync(
- "import-job",
- It.IsAny>(),
- It.IsAny(),
- It.IsAny()), Times.Once);
-```
-
-```csharp
-// NSubstitute
-manager.SetupTryRunAsync(returns: true, taskName: "import-job");
-
-await manager.Received(1).TryRunAsync(
- "import-job",
- Arg.Any>(),
- Arg.Any(),
- Arg.Any());
-```
-
----
-
-## FakeTaskStateManager
-
-`FakeTaskStateManager` is a framework-agnostic in-memory implementation of `ITaskStateManager`. It executes work inline with no distributed state, no polling, and no boilerplate. Use it when you don't need to verify which task name was passed — it's the simplest option for "does the handler actually do the work" tests.
+### Works with any mocking framework
```csharp
// AutoMocker (Moq) — inject the fake instead of setting up a mock
@@ -126,7 +59,7 @@ var fake = new FakeTaskStateManager { WaitTimeout = TimeSpan.FromSeconds(10) };
## Manual mocking (without TaskTurnstile.Testing)
-If you prefer to wire up your mocking framework directly without the helper extensions, the key is capturing and invoking the `work` delegate inside the `Returns` callback.
+If you prefer to wire up your mocking framework directly, the key is capturing and invoking the `work` delegate inside the `Returns` callback.
### Moq / AutoMocker — TryRunAsync runs work, returns `true`
@@ -163,7 +96,7 @@ manager.TryRunAsync(
Arg.Any())
.Returns(async ci =>
{
- await ci.Arg>()(ci.Arg());
+ await ci.Arg>()(CancellationToken.None);
return true;
});
```