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.24k stars 754 forks source link

Each async part of a test is run in its own context, which is giving us issues. #2504

Closed BMagerMT closed 2 years ago

BMagerMT commented 2 years ago

SpecFlow Version

3.9.8

Which test runner are you using?

xUnit

Test Runner Version Number

2.4.3

.NET Implementation

.NET 5.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

{ "generator": { "addNonParallelizableMarkerForTags": ["ExperimentTests"] } }

Issue Description

Each asynchronous part of a test (e.g. given, when, and then) is run its own synchronization context via AsyncHelpers.RunSync and this synchronization context is shutdown after the end of the method. https://github.com/SpecFlowOSS/SpecFlow/blob/fd29b6f3948247d44714f3d34d0ec4085d8dfebd/TechTalk.SpecFlow/Bindings/AsyncHelpers.cs

The problem is if later step needs something created in the async method to still execute in its context it can't because the context was shutdown.

Steps to Reproduce

Our actual problem is a little more complex but this following shows the issue.

The first scenario never returns because the SynchronizationContext created in the first step is shutdown after the first step. If you run all the parts in one step, then they work because the are all in the same context which is still running.

To me the whole test should be run the same SynchronizationContext . Overall I don't think that would be that hard to fix. If there are any async methods then set the context for the whole test or it could potentially just always be set. In AsyncHelper if the current SynchronizationContext is already an ExclusiveSynchronizationContext just use it instead of replacing the SynchronizationContext.

@tag1 Scenario: Context Issue when in steps Given Object created in context When Method is now called Then The method should work

Scenario: The same all run in on step Given All run in one step

[Binding] public class TestSteps { private ObjectWithAysnc _objectWithAysnc; private int _result;

    [Given(@"Object created in context")]
    public async Task GivenObjectCreatedInContext()
    {
        _objectWithAysnc = await ObjectWithAysnc.Create(10);
    }

    [When(@"Method is now called")]
    public async Task WhenMethodIsNowCalled()
    {
        _result = await _objectWithAysnc.AddFive();
    }

    [Then(@"The method should work")]
    public void ThenTheMethodShouldWork()
    {
        _result.ShouldBe(15);
    }

    [Given(@"All run in one step")]
    public async Task GivenAllRunInOneStep()
    {
        await GivenObjectCreatedInContext();
        await WhenMethodIsNowCalled();
        ThenTheMethodShouldWork();
    }

}

 public class ObjectWithAysnc
{
    private readonly SynchronizationContext _synchronizationContext;
    private int _value;

    public ObjectWithAysnc(int value)
    {
        _value = value;
        _synchronizationContext = SynchronizationContext.Current;
    }

    public static async Task<ObjectWithAysnc> Create(int value)
    {
        await Task.Delay(10);
        return new ObjectWithAysnc(value);
    }

    public Task<int> AddFive()
    {
        var taskCompletionSource = new TaskCompletionSource<int>();
        _synchronizationContext.Post(_ => taskCompletionSource.SetResult(_value + 5), null);

        return taskCompletionSource.Task;
    }
}

Link to Repro Project

No response

BMagerMT commented 2 years ago

Note for the Repo code, I actually used all of the latest versions of the components. SpecFlowContextIssue.zip The zip file has the sample source.

SabotageAndi commented 2 years ago

I think you have the same issue as https://github.com/SpecFlowOSS/SpecFlow/issues/1623

And I think we have already a PR for it: https://github.com/SpecFlowOSS/SpecFlow/pull/1614

BMagerMT commented 2 years ago

I think you have the same issue as #1623

And I think we have already a PR for it: #1614

I agree this seems to be the same issue. Any idea if PR #1614 is going to be implemented some day. It seems like it is languishing. From the description it sounds like the proper route (async all the way).

SabotageAndi commented 2 years ago

I have hope that we work on this topic in the first 6 months of 2022.

SabotageAndi commented 2 years ago

@NorekZ & @gasparnagy worked on implementing async/await proper and a first version is released to NuGet.org - https://www.nuget.org/packages/SpecFlow/3.10.2-beta

Please try it out and report back if it solved your issues. Please do it in a new issue.

github-actions[bot] commented 2 years ago

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.