machine / machine.specifications

Machine.Specifications is a Context/Specification framework for .NET that removes language noise and simplifies tests.
MIT License
885 stars 178 forks source link

Problem with `AsyncSynchronizationContext` #496

Closed nmosafi closed 1 year ago

nmosafi commented 1 year ago

Hi

In some of our tests we are installing our own SynchronisationContext in Establish context. We do this in order to make our tests more deterministic as it allows us to control which thread various callbacks are executed on.

At some point many years ago MSpec introduced its own AsyncSynchronizationContext which is installed each time any delegate is called.

In our Establish context we have something like

Establish context = () => 
{
  SynchronisationContext.SetSynchronizationContext(new MySyncContext());
  // rest of context here
}

So we end up overwriting the AsyncSynchronizationContext which is installed by MSpec. When our delegate has then finished running, it's essentially then reset back to null here.

We need a solution to this, perhaps MSpec can expose some method we can use to set the SychronisationContext (rather than using SynchronisationContext.SetSynchronizationContext we could use MSpecSynchronisationContext.SetSynchronizationContext or something like that) and then internally MSpec can make sure that context is always used as AsyncSynchronizationContext._inner

robertcoltheart commented 1 year ago

From memory, the async context is only used if you are trying to use async/await in your Mspec tests. The workaround for now is just to use .Result, .Wait() etc to manually resolve the task in your mspec tests.

nmosafi commented 1 year ago

Unfortunately not, the following test fails

    public class TestSyncContext
    {
        It should_have_no_sync_context = () => SynchronizationContext.Current.ShouldBeNull();
    }

Fails with the error Machine.Specifications.SpecificationException: Should be [null] but is Machine.Specifications.Runner.Impl.AsyncSynchronizationContext

robertcoltheart commented 1 year ago

I've released 1.1.1 which should fix that issue. The async sync context will only get installed for delegates that are async. I'd suggest giving that a go, maybe coupling that with some lifecycle events from here to achieve what you need: https://github.com/machine/machine.specifications/wiki/Advanced-Lifecycle-Options

nmosafi commented 1 year ago

Thanks @robertcoltheart I'll give it a go!