SpecFlow icon indicating copy to clipboard operation
SpecFlow copied to clipboard

Error: Collection was modified; enumeration operation may not execute.

Open Chili82 opened this issue 4 years ago • 21 comments

SpecFlow Version

3.8.14

Which test runner are you using?

SpecFlow+ Runner

Test Runner Version Number

3.8.12

.NET Implementation

equal or greater .NET Framework 4.6.1

Project Format of the SpecFlow project

Classic project format using packages.config

.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

Issue Description

I use SpecFlow + SpecRun, and I did an update of the NuGet package to the latest versions. image

After I run the test, all the steps are done properly but in the final report the test failed with the following error: image I am using Autofac library, last version image

In the previous version of SpecFlow (3.0.220) everything worked fine

Steps to Reproduce

I followed the instructions from the official SpecFlow documentation: image

Link to Repro Project

No response

Chili82 avatar May 24 '21 10:05 Chili82

It would help us, if you could try out the minor versions since 3.0 and 3.8 to find where we added this bug.

SabotageAndi avatar May 25 '21 06:05 SabotageAndi

It would help us if you could try out the minor versions since 3.0 and 3.8 to find where we added this bug.

ok, I'll check and let you know.

Chili82 avatar May 25 '21 08:05 Chili82

It would help us, if you could try out the minor versions since 3.0 and 3.8 to find where we added this bug.

For a version higher than 3.6 I get the same error again. In the picture below is the last version that worked correctly: image Autofac version: image image

Chili82 avatar May 25 '21 11:05 Chili82

One part of the error is here: https://github.com/SpecFlowOSS/BoDi/blob/master/BoDi/BoDi.cs#L1011

The dictionary is enumerated to dispose its values. This exception tells you that something has modified the collection while it was being enumerated for disposal. (For the scenario container)

The only methods that modify it is either

  • Resolve (Type or Factory)
  • RegisterInstanceAs

=> So is there something your test is doing other than normal in a multithreaded fashion?

bollhals avatar May 25 '21 21:05 bollhals

One part of the error is here: https://github.com/SpecFlowOSS/BoDi/blob/master/BoDi/BoDi.cs#L1011

The dictionary is enumerated to dispose its values. This exception tells you that something has modified the collection while it was being enumerated for disposal. (For the scenario container)

The only methods that modify it is either

  • Resolve (Type or Factory)
  • RegisterInstanceAs

=> So is there something your test is doing other than normal in a multithreaded fashion?

no, it's very strange that in version 3.6 it works properly while in version 3.7 the same code doesn't work ok. I call Dispose in AfterTestRun hook.

Chili82 avatar May 26 '21 07:05 Chili82

On which object do you call the dispose method?

SabotageAndi avatar May 26 '21 11:05 SabotageAndi

On which object do you call the dispose method?

On webdriver and ApplicationContainer (Application dependencies disposed)

Chili82 avatar May 26 '21 12:05 Chili82

Is it possible to view the source of it or to extract it in a small example published somewhere? I do not yet see what could modify the dictionary while disposing.

bollhals avatar May 26 '21 12:05 bollhals

@bollhals issue first appeared after updating BoDi from 1.4.1 to 1.5 (as a result appeared in SpecFlow starting from 3.7.13) having a build of SpecFlow + NUnit + AutoFac leads to an error after every scenario only if you have hooks

simple way to reproduce:

  • create a simple project from template of specflow + NUnit (feature+steps+hooks)
  • add specflow.autofac NuGet
  • create scenarioDependecy register as per (https://docs.specflow.org/projects/specflow/en/latest/Integrations/Autofac.html#a-typical-dependency-builder-method-probably-looks-like-this)

on cleanup you receive:

TearDown : System.InvalidOperationException : Collection was modified; enumeration operation may not execute.
--TearDown
   at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
   at System.Collections.Generic.Dictionary`2.ValueCollection.Enumerator.MoveNext()
   at System.Linq.Enumerable.<OfTypeIterator>d__95`1.MoveNext()
   at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
   at BoDi.ObjectContainer.Dispose()
   at TechTalk.SpecFlow.Infrastructure.ContextManager.InternalContextManager`1.DisposeInstance()
   at TechTalk.SpecFlow.Infrastructure.ContextManager.InternalContextManager`1.Cleanup()
   at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.OnScenarioEnd()

dit2100 avatar Mar 14 '22 12:03 dit2100

My best guess: The dispose of the container disposes all resolved singletons, one of them (possibly related to autofac plugin) does something then with the container, which trips up the disposal.

I'm happy to take a quick peak if you can provide an example. (Not to be blunt, but as I'm not associated with specflow, I'm happy to help but I do not intend to invest my free time to setup an example to test it.)

bollhals avatar Mar 14 '22 13:03 bollhals

Sure, thanks for looking into this https://github.com/dit2100/SpecFlowExample

dit2100 avatar Mar 14 '22 14:03 dit2100

Thanks for the example, I had a quick peak.

I can confirm that the disposal of the specflow container disposes the autofac, which tries again to dispose the specflow one.

The 2nd time autofac remembers and succeds, which then clears the specflow one, so the first iteration dies with a modified exception. => Bodi should probably fix this with a early exit if already disposed.

On the otherhand, I don't know if the autofaq plugin is working as expected, This is the output I get when I run your ONE test. 18 registrations, so 9 ScenarioContext and 9 Containers.... Don't know where or how they all end up there. image

A quick search showed me that every time we resolve the ScenarioContext (e.g. to call the hook) we add the ScenarioContext and the IObjectContainer to the autofaq cleanup list. So maybe the registration is flawed or something?

bollhals avatar Mar 14 '22 16:03 bollhals

Thanks a bunch for the confirmation. I had the same feeling, but didn't dig deep into internals (since new in C# world) Tried to stick to older versions of specflow just to find out that livingdoc for that version is broken and the fix is only available for the version with a broken autofac integration=) for now I ditched that impl in favor for built-in BoDi solution (since this issue persists for over an year now) I guess this thread(https://github.com/SpecFlowOSS/SpecFlow/issues/2353) is related and maybe will be addressed in upcoming releases Nonetheless thank you for your time @bollhals Cheers

dit2100 avatar Mar 14 '22 16:03 dit2100

If I find the time I'll make a PR in the bodi container to early exit the dispose. With this you should at least be able to continue. (It still does strange things, but at least it works)

bollhals avatar Mar 14 '22 17:03 bollhals

@gasparnagy any ideas?

SabotageAndi avatar Mar 15 '22 10:03 SabotageAndi

Just hit this problem, is there a workaround or an update on when it's likely to be fixed?

chris-richards-puregym avatar Jul 14 '22 08:07 chris-richards-puregym

there was an update in this area with a new approach, you can find more here: https://docs.specflow.org/projects/specflow/en/latest/Integrations/Autofac.html it resolves initial issue with disposal, but creates another one of initiating all necessary helper services (for specflow reports in my case)

so I've abandoned idea of autofac integration at this point of time in favor of build-in solution

dit2100 avatar Jul 14 '22 10:07 dit2100

I've managed to create a work around for this by creating a plugin that registers the IObjectContainer in Autofac as externally owned, that way Autofac won't attempt to dispose of it. I don't think this will lead to any memory leaks but let me know if I've missed something.

  // Register the SpecFlow DI container as externally owned so that
  // Autofac doesn't attempt to dispose of it
  containerBuilder.Register(ctx => objectContainer)
      .As<IObjectContainer>()
      .ExternallyOwned();

chris-richards-puregym avatar Jul 14 '22 16:07 chris-richards-puregym

I've managed to create a work around for this by creating a plugin that registers the IObjectContainer in Autofac as externally owned, that way Autofac won't attempt to dispose of it. I don't think this will lead to any memory leaks but let me know if I've missed something.

  // Register the SpecFlow DI container as externally owned so that
  // Autofac doesn't attempt to dispose of it
  containerBuilder.Register(ctx => objectContainer)
      .As<IObjectContainer>()
      .ExternallyOwned();

I am facing exactly this issue and I am trying to apply your workaround. Where do you get the instance objectContainer from? Could you post a larger chunk of code?

Thanks!

sergimola avatar Jul 15 '22 13:07 sergimola

You have to create a plugin to override the registrations, see https://github.com/chrisrichards/SpecFlowExample

chrisrichards avatar Jul 19 '22 12:07 chrisrichards

You have to create a plugin to override the registrations, see https://github.com/chrisrichards/SpecFlowExample

This makes it work for me.. Thanks!

sergimola avatar Jul 19 '22 14:07 sergimola