SpecFlowOSS / SpecFlow

#1 .NET BDD Framework. SpecFlow automates your testing & works with your existing code. Find Bugs before they happen. Behavior Driven Development helps developers, testers, and business representatives to get a better understanding of their collaboration
https://www.specflow.org/
Other
2.22k stars 751 forks source link

IAsyncLifetime not respected on Step definitions class, how should I call async disposal on injected context #2706

Open elijahmondero opened 1 year ago

elijahmondero commented 1 year ago

SpecFlow Version

3.9

Which test runner are you using?

xUnit

Test Runner Version Number

2.4.5

.NET Implementation

.NET 6.0

Project Format of the SpecFlow project

Sdk-style project format

.feature.cs files are generated using

SpecFlow.Tools.MsBuild.Generation NuGet package

Test Execution Method

Visual Studio Test Explorer

SpecFlow Section in app.config or content of specflow.json

N/A

Issue Description

I wish to inject a context in the [Given] step. However the documentation says that injected context are disposed if they implement IDisposable. However the contexts I need to inject need to be disposed asynchronously. When I use xunit directly (prior to using specflow) I was able to implement IAsynchLifetime and call await myInjectedContextObject.DisposeAsync().

Could you tell me what the specflow way of doing this is please?

Steps to Reproduce

Add IAsyncLifetime to any steps definition class. The async Task InitialiseAsynch() and async Task DisposeAsymch() are not called when running the feature

Link to Repro Project

No response

jernejg commented 1 year ago

My current workaround are hooks + parameter injection:

[Binding]
public class GateKeepingTests
{
    private readonly ExpensiveFixture _fixture;

    public GateKeepingTests(ExpensiveFixture fixture)
    {
        _fixture = fixture;
    }

    [BeforeFeature]
    public static async Task Before(ExpensiveFixture fixture)
    {
        await fixture.InitializeAsync();
    }

    [AfterFeature]
    public static async Task After(ExpensiveFixture fixture)
    {
        await fixture.DisposeAsync();
    }
}
marcelloguimaraes commented 2 months ago

My current workaround are hooks + parameter injection:

[Binding]
public class GateKeepingTests
{
    private readonly ExpensiveFixture _fixture;

    public GateKeepingTests(ExpensiveFixture fixture)
    {
        _fixture = fixture;
    }

    [BeforeFeature]
    public static async Task Before(ExpensiveFixture fixture)
    {
        await fixture.InitializeAsync();
    }

    [AfterFeature]
    public static async Task After(ExpensiveFixture fixture)
    {
        await fixture.DisposeAsync();
    }
}

I had to do the same thing here.

victorBychinski commented 1 month ago

Hello @jernejg , Do these async Hooks work when running tests in parallel? I have something like this: `[Binding] public class BeforeFeatureHooks {

public BeforeFeatureHooks(FeatureContext featureContext)
{
    _featureContext = featureContext;
}
[BeforeFeature]
public static async Task SomeBeforeFeatureHook(FeatureContext featureContext, TestDataGenerator testDataGenerator)
{
   var someTestData = await testDataGenerator.Generate();
   featureContext.Add("some key", someTestData);
}`

and then, in my steps definitions, where I user DI and passing FeatureContext featureContext in the Steps definitions constructors and trying go get the key from the featureContext - for part of the tests this key is unavailable.