Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MissingMethodException when using Reqnroll.BoDi with a Plugin #91

Closed
mbhoek opened this issue Apr 6, 2024 · 11 comments
Closed

MissingMethodException when using Reqnroll.BoDi with a Plugin #91

mbhoek opened this issue Apr 6, 2024 · 11 comments
Labels
bug Something isn't working

Comments

@mbhoek
Copy link
Contributor

mbhoek commented Apr 6, 2024

Reqnroll Version

1.0.2-local (vNext)

Which test runner are you using?

xUnit

Test Runner Version Number

2.5.6

.NET Implementation

.NET 6.0

Test Execution Method

Visual Studio Test Explorer

Content of reqnroll.json configuration file

No response

Issue Description

Using a DI container plugin results in a System.MissingMethodException : Method not found: 'Reqnroll.BoDi.ObjectContainer Reqnroll.Plugins.ObjectContainerEventArgs.get_ObjectContainer()' (full exception below) when using Reqnroll vNext (which includes Reqnroll.BoDi as part of the solution). The exception does not occur using Reqnroll v1.0.1.

I first encountered this exception whilst porting the Microsoft.Extensions.DependencyInjection integration plugin to Reqnroll. Thinking I made a mistake during the port, I created a simple test project using the Autofac integration plugin (see link to repro project). This resulted in the exact same exception.

Full exception:

[xUnit.net 00:00:00.45]     Test a service [FAIL]
[xUnit.net 00:00:00.46]     [Test Class Cleanup Failure (Reqnroll.PluginTester.Features.TestFeature)] System.NullReferenceException
  Failed Test a service [1 ms]
  Error Message:
   System.AggregateException : One or more errors occurred. (Method not found: 'Reqnroll.BoDi.ObjectContainer Reqnroll.Plugins.ObjectContainerEventArgs.get_ObjectContainer()'.) (Method not found: 'Reqnroll.BoDi.ObjectContainer Reqnroll.Plugins.ObjectContainerEventArgs.get_ObjectContainer()'.)
---- System.MissingMethodException : Method not found: 'Reqnroll.BoDi.ObjectContainer Reqnroll.Plugins.ObjectContainerEventArgs.get_ObjectContainer()'.
---- System.MissingMethodException : Method not found: 'Reqnroll.BoDi.ObjectContainer Reqnroll.Plugins.ObjectContainerEventArgs.get_ObjectContainer()'.
  Stack Trace:

----- Inner Stack Trace #1 (System.MissingMethodException) -----
   at Reqnroll.Autofac.AutofacPlugin.<>c.<Initialize>b__1_0(Object sender, CustomizeGlobalDependenciesEventArgs args)
   at Reqnroll.Plugins.RuntimePluginEvents.RaiseCustomizeGlobalDependencies(ObjectContainer container, ReqnrollConfiguration reqnrollConfiguration)
   at Reqnroll.Infrastructure.ContainerBuilder.CreateGlobalContainer(Assembly testAssembly, IRuntimeConfigurationProvider configurationProvider)
   at Reqnroll.TestRunnerManager.CreateTestRunnerManager(Assembly testAssembly, IContainerBuilder containerBuilder)
   at Reqnroll.TestRunnerManager.<>c__DisplayClass38_0.<GetTestRunnerManager>b__0(Assembly assembly)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Reqnroll.TestRunnerManager.GetTestRunnerManager(Assembly testAssembly, IContainerBuilder containerBuilder, Boolean createIfMissing)
   at Reqnroll.TestRunnerManager.OnTestRunStartAsync(Assembly testAssembly, String testWorkerId, IContainerBuilder containerBuilder)
   at Reqnroll_PluginTester_XUnitAssemblyFixture.InitializeAsync() in C:\Users\Mark\Projects\Reqnroll.PluginTester\obj\Debug\net6.0\xUnit.AssemblyHooks.cs:line 18
   at Reqnroll.xUnit.ReqnrollPlugin.XunitTestAssemblyRunnerWithAssemblyFixture.<AfterTestAssemblyStartingAsync>b__2_0()
----- Inner Stack Trace #2 (System.MissingMethodException) -----
   at Reqnroll.Autofac.AutofacPlugin.<>c.<Initialize>b__1_0(Object sender, CustomizeGlobalDependenciesEventArgs args)
   at Reqnroll.Plugins.RuntimePluginEvents.RaiseCustomizeGlobalDependencies(ObjectContainer container, ReqnrollConfiguration reqnrollConfiguration)
   at Reqnroll.Infrastructure.ContainerBuilder.CreateGlobalContainer(Assembly testAssembly, IRuntimeConfigurationProvider configurationProvider)
   at Reqnroll.TestRunnerManager.CreateTestRunnerManager(Assembly testAssembly, IContainerBuilder containerBuilder)
   at Reqnroll.TestRunnerManager.<>c__DisplayClass38_0.<GetTestRunnerManager>b__0(Assembly assembly)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Reqnroll.TestRunnerManager.GetTestRunnerManager(Assembly testAssembly, IContainerBuilder containerBuilder, Boolean createIfMissing)
   at Reqnroll.TestRunnerManager.GetTestRunnerForAssembly(Assembly testAssembly, String testWorkerId, IContainerBuilder containerBuilder)
   at Reqnroll.PluginTester.Features.TestFeature.FeatureSetupAsync()
   at Reqnroll.PluginTester.Features.TestFeature.FixtureData.Xunit.IAsyncLifetime.InitializeAsync()
  Failed Test a service [1 ms]
  Error Message:
   [Test Class Cleanup Failure (Reqnroll.PluginTester.Features.TestFeature)]: System.NullReferenceException : Object reference not set to an instance of an object.
  Stack Trace:
     at Reqnroll.PluginTester.Features.TestFeature.FeatureTearDownAsync()
   at Reqnroll.PluginTester.Features.TestFeature.FixtureData.Xunit.IAsyncLifetime.DisposeAsync()

Steps to Reproduce

  1. Build Reqnroll vNext locally, this will result in v1.0.2-local NuGet packages for Reqnroll and related projects
  2. Build the Repro Project with the v1.0.2-local NuGet packages (I use a local NuGet folder)

Steps to verify that it does work in v1.0.1:

  1. Change the package versions in Reqnroll.PluginTester.csproj to v1.0.1

Link to Repro Project

https://github.com/mbhoek/Reqnroll.PluginTester

@clrudolphi
Copy link
Contributor

I'm not seeing the same problem using your sample project. Here is a screenshot showing the test as successful along with the Package dependencies showing 1.0.2-local.
Screenshot 2024-04-07 125223

Not that the above helps much, but thought you'd want to see that it works elsewhere.
I'm not seeing anything obvious. Perhaps there is still something wonky with your dependencies?

@gasparnagy
Copy link
Contributor

@mbhoek Try to delete the C:\Users\<youruser>\.nuget\packages\reqnroll* folders to make sure that you are really using the latest compiled Reqnroll local package.

@mbhoek
Copy link
Contributor Author

mbhoek commented Apr 8, 2024

@clrudolphi @gasparnagy Thanks for verifying. After deleting the .nuget folder and restarting Visual Studio multiple times it did start to work. Guess I need to be more diligent whilst working on this plugin.

@mbhoek mbhoek closed this as completed Apr 8, 2024
@mbhoek
Copy link
Contributor Author

mbhoek commented Apr 8, 2024

@gasparnagy Reopening this to discuss a related Reqnroll.BoDi issue: it still outputs Reqnroll.Bodi as a separate NuGet package. This is package is also referenced as a dependency in Reqnroll.nuspec. Is that something we want to keep this way, or do we want BoDi to just be part of the Reqnroll package?

@mbhoek mbhoek reopened this Apr 8, 2024
@gasparnagy
Copy link
Contributor

@mbhoek I don't know, to be honest. There are pros and cons on both sides. My thinking was, that for the majority of the usages, it does not matter. You only need to add the dependency to the Reqnroll package and the BoDi package will be loaded implicitly, but if anyone used BoDi without SpecFlow (???) they could still migrate to Reqnroll.BoDi... Do you see any inconvenience or disadvantage of having it as a separate package?

@ajeckmans
Copy link
Contributor

As long as someone is not loading a binary incompatible version of the bodi package alongside a reqnroll package with bodi compiled in it. It would fail at runtime when calling the incompatible code without any warning at compile time.

@mbhoek
Copy link
Contributor Author

mbhoek commented Apr 8, 2024

After a little postmortem I think that not having the correct Reqnroll.BoDi package in my local NuGet folder caused the original problem of this issue. That's why I was wondering why we still need it as a separate package; in my mind we don't need it anymore (I don't think BoDi is used a lot outside of SpecFlow/Reqnroll, but I might be wrong).

Taking this one step further: if we have integration plugins for all the major DI frameworks, do we even need BoDi at all? I can imagine a future where Reqnroll users not only select which testing framework they use (NUnit/xUnit/etc) but als which DI framework (instead of BoDi). Major change ofcourse, but it might make integration of DI frameworks easier in the end.

@gasparnagy
Copy link
Contributor

@ajeckmans As long as someone is not loading a binary incompatible version of the bodi package alongside a reqnroll package with bodi compiled in it. It would fail at runtime when calling the incompatible code without any warning at compile time.

Maybe because it's too late, but I'm not sure I get your point. Is this an argument for the separate package or against it?

@mbhoek If we have integration plugins for all the major DI frameworks, do we even need BoDi at all?

The DI frameworks are too different and it would be very hard to keep a consistent behavior of Reqnroll itself if we would need to rely on the specific behavior of 5+ different DI frameworks. The current DI plugins provide an implementation of the ITestObjectResolver interface that is used to resolve only the objects of the user (step def classes, etc.), but even in this situation the Reqnroll internal infrastructure is created using BoDi. So we will need that. (Why we did not pick one selected DI framework and managed the SpecFlow classes with that is an interesting question: if we would have picked a version of a particular DI it would have caused conflict if the users would wanted to use a different version of the same DI.)

And if we anyway have BoDi, we can offer that as a default. It is good enough for the most of the needs anyway.

@ajeckmans
Copy link
Contributor

Neither :) it is just that if you fully integrate into the source of Reqroll (so not referencing the package) and also still package the nuget, then people can run into issues. So the only safe options are the ones mentioned. Separate package and referencing in Reqroll or fully integrating into reqnroll and dropping the package.

Personally I think if someone really wants to use it outside of the reqnroll context they can use the specflow one or copy/paste the code in their solution. For anyone that is extending Reqroll (or using it in a reqnroll project), packaging it with Reqroll and keeping the interfaces public should suffice.

@gasparnagy
Copy link
Contributor

@ajeckmans Got it now. :)

I feel like convinced about including it only to the Reqnroll package and not releasing it as a separate package. I will try to make tomorrow. (Note to self: this was the commit that did it.)

@mbhoek
Copy link
Contributor Author

mbhoek commented Apr 8, 2024

@gasparnagy And if we anyway have BoDi, we can offer that as a default. It is good enough for the most of the needs anyway.

I'm fine with BoDi, but as a plugin developer I found it cumbersome to 'hide' the DI framework inside BoDi with a level of indirection, so that's why I suggested it. Obviously we can continue with how things are right now. 👍

@mbhoek mbhoek closed this as completed Apr 8, 2024
gasparnagy added a commit that referenced this issue Apr 10, 2024
* Include BoDi to Reqnroll package (#91)

* Move BoDi code to Reqnroll project

* split & cleanup BoDi.cs

* Remove Reqnroll.BoDi project
gasparnagy added a commit that referenced this issue May 22, 2024
…ons-dependencyinjection-plugin

* origin/main: (21 commits)
  Fix #56 autofac ambiguous stepdef and hook required #127 issue (#139)
  Reduce target framework of Reqnroll to netstandard2.0 (#130)
  Fix StackOverflowException when using [StepArgumentTransformation] with same input and output type (#136)
  MsTest: Replace DelayedFixtureTearDown special case with ClassCleanupBehavior.EndOfClass (#128)
  Temporarily disabled tests until #132 is resolved
  Add NUnit & xUnit core tests to portability suite
  Capture ExecutionContext after every binding invoke (#126)
  small improvement in CodeDomHelper to be able to chain async calls
  fix method name sources in UnitTestFeatureGenerator
  External data plugin, support for JSON files  (#118)
  UnitTests: Check if SDK version is installed and if not ignore the test (#109)
  Fix 111 ignore attr not inherited from rule (#113)
  Update README.md (#110)
  Fix 81 - modified CucumberExpressionParameterTypeRegistry to handle multiple custom types used as cucumber expressions when those types share the same short name. (#104)
  Update index.md
  Simplify test project targets (#105)
  Make SystemTests temp folder configurable and use NUGET_PACKAGES env var to override global NuGet folder
  Fleshing out Generation System Tests (2) (#99)
  Fix for 81 - Cucumber Expression using Enums errors when two enums exist with the same short name (#100)
  Include BoDi to Reqnroll package (#91) (#95)
  ...

# Conflicts:
#	Reqnroll.sln
#	Tests/Reqnroll.PluginTests/Reqnroll.PluginTests.csproj
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants