Skip to content

Commit 2efd260

Browse files
committed
Add IBeforeStep and IAfterStep hooks
1 parent d07dd42 commit 2efd260

File tree

6 files changed

+156
-1
lines changed

6 files changed

+156
-1
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using BddDotNet.Extensibility;
2+
using Microsoft.Extensions.DependencyInjection;
3+
4+
namespace BddDotNet.Tests.Core;
5+
6+
[TestClass]
7+
public sealed class AfterStepTests
8+
{
9+
[TestMethod]
10+
public async Task AfterStepTest()
11+
{
12+
var traces = new List<string>();
13+
14+
await TestPlatform.RunTestAsync(services =>
15+
{
16+
services.AddSingleton(traces);
17+
18+
services.Scenario<ScenarioAndStepTests>("feature1", "scenario1", async context =>
19+
{
20+
await context.Then("then1");
21+
await context.Then("then2");
22+
});
23+
24+
services.Then(new("then1"), () =>
25+
{
26+
traces.Add("1");
27+
});
28+
29+
services.Then(new("then2"), () =>
30+
{
31+
throw new Exception("2");
32+
});
33+
34+
services.AfterStep<AfterStep1>();
35+
});
36+
37+
Assert.IsTrue(traces is ["1", "AfterStep null", "AfterStep 2"]);
38+
}
39+
}
40+
41+
file sealed class AfterStep1(List<string> traces) : IAfterStep
42+
{
43+
public Task AfterStep(Exception? exception)
44+
{
45+
traces.Add($"AfterStep {exception?.Message ?? "null"}");
46+
return Task.CompletedTask;
47+
}
48+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using BddDotNet.Extensibility;
2+
using Microsoft.Extensions.DependencyInjection;
3+
4+
namespace BddDotNet.Tests.Core;
5+
6+
[TestClass]
7+
public sealed class BeforeStepTests
8+
{
9+
[TestMethod]
10+
public async Task BeforeStepTest()
11+
{
12+
var traces = new List<string>();
13+
14+
await TestPlatform.RunTestAsync(services =>
15+
{
16+
services.AddSingleton(traces);
17+
18+
services.Scenario<ScenarioAndStepTests>("feature1", "scenario1", async context =>
19+
{
20+
await context.Then("then1");
21+
});
22+
23+
services.Then(new("then1"), () =>
24+
{
25+
traces.Add("1");
26+
});
27+
28+
services.BeforeStep<BeforeStep1>();
29+
});
30+
31+
Assert.IsTrue(traces is ["BeforeStep", "1"]);
32+
}
33+
}
34+
35+
file sealed class BeforeStep1(List<string> traces) : IBeforeStep
36+
{
37+
public Task BeforeStep()
38+
{
39+
traces.Add("BeforeStep");
40+
return Task.CompletedTask;
41+
}
42+
}

src/BddDotNet/Extensibility.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,21 @@ public static partial class ServiceCollectionExtensions
3232
serviceCollection.AddScoped<IArgumentTransformation>(services => services.GetRequiredService<T>());
3333
return serviceCollection;
3434
}
35+
36+
public IServiceCollection BeforeStep<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>()
37+
where T : class, IBeforeStep
38+
{
39+
serviceCollection.TryAddScoped<T>();
40+
serviceCollection.AddScoped<IBeforeStep>(services => services.GetRequiredService<T>());
41+
return serviceCollection;
42+
}
43+
44+
public IServiceCollection AfterStep<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>()
45+
where T : class, IAfterStep
46+
{
47+
serviceCollection.TryAddScoped<T>();
48+
serviceCollection.AddScoped<IAfterStep>(services => services.GetRequiredService<T>());
49+
return serviceCollection;
50+
}
3551
}
3652
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace BddDotNet.Extensibility;
2+
3+
public interface IAfterStep
4+
{
5+
Task AfterStep(Exception? exception);
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace BddDotNet.Extensibility;
2+
3+
public interface IBeforeStep
4+
{
5+
Task BeforeStep();
6+
}

src/BddDotNet/Internal/Services/StepExecutionService.cs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,46 @@ namespace BddDotNet.Internal.Services;
99
internal sealed class StepExecutionService(
1010
IServiceProvider serviceProvider,
1111
IEnumerable<Step> steps,
12-
IEnumerable<IArgumentTransformation> argumentTransformations)
12+
IEnumerable<IArgumentTransformation> argumentTransformations,
13+
IEnumerable<IBeforeStep> beforeStepHooks,
14+
IEnumerable<IAfterStep> afterStepHook)
1315
{
1416
public async Task ExecuteAsync(StepType stepType, string text, object?[] additionalStepArguments)
17+
{
18+
var keyword = stepType.ToString();
19+
20+
await BeforeStep();
21+
22+
try
23+
{
24+
await FindAndExecuteStepAsync(stepType, text, additionalStepArguments);
25+
}
26+
catch (Exception exception)
27+
{
28+
await AfterStep(exception.GetBaseException());
29+
throw;
30+
}
31+
32+
await AfterStep(null);
33+
}
34+
35+
private async Task BeforeStep()
36+
{
37+
foreach (var beforeStepHook in beforeStepHooks)
38+
{
39+
await beforeStepHook.BeforeStep();
40+
}
41+
}
42+
43+
private async Task AfterStep(Exception? exception)
44+
{
45+
foreach (var afterStepHook in afterStepHook.Reverse())
46+
{
47+
await afterStepHook.AfterStep(exception);
48+
}
49+
}
50+
51+
private async Task FindAndExecuteStepAsync(StepType stepType, string text, object?[] additionalStepArguments)
1552
{
1653
var (step, match) = FindGherkinStep(stepType, text);
1754

0 commit comments

Comments
 (0)