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

Autofac without BeforeTestRun hook does not run GlobalDependencies #127

Closed
michielboekhoff opened this issue May 10, 2024 · 5 comments
Closed
Labels
bug Something isn't working

Comments

@michielboekhoff
Copy link

Reqnroll Version

1.0.1

Which test runner are you using?

xUnit

Test Runner Version Number

2.7.0

.NET Implementation

.NET 8.0

Test Execution Method

Other – PLEASE SPECIFY

Content of reqnroll.json configuration file

{
}

Issue Description

Hey there! First of all, thanks so much for this project. I'm really happy that it's been picked up, and I'm really happy to see how active this repo has been recently.

Since I've started using the Autofac plugin, I've been seeing mildly strange behaviour with the [GlobalDependencies] attribute. I've been writing integration tests using Testcontainers, and I want to set up a single container to share across all features. To this end, I've got a fairly complex GlobalDependencies class:

GlobalDependencies.cs

public class GlobalDependencies
{
    [GlobalDependencies]
    public static void RegisterGlobalDependencies(ContainerBuilder builder)
    {
        builder
            .Register(_ =>
            {
                var container = new WireMockContainerBuilder()
                    .WithAutoRemove(true)
                    .WithCleanUp(true)
                    .Build();

                // This is not ideal, but perhaps we can do this in the `BeforeTestRun`
                container.StartAsync().Wait();

                return container;
            })
            .Named<WireMockContainer>("my-downstream-service-1")
            .SingleInstance();

        builder.Register((c, p) =>
            {
                var image = ServiceImage();
                var regulations = p.Named<WireMockContainer>("my-downstream-service-1");
                var container = new TestContainerBuilder()
                    .WithImage(image)
                    .WithAutoRemove(true)
                    .WithCleanUp(true)
                    .WithPortBinding(80, assignRandomHostPort: true)
                    .WithWaitStrategy(Wait.ForUnixContainer().UntilHttpRequestIsSucceeded(r => r.ForPort(80)))
                    .Build();

                container.StartAsync().Wait();

                return container;
            })
            .As<IContainer>()
            .AutoActivate();

        builder.Register((_, p) => p.Named<WireMockContainer>("my-downstream-service-1").CreateWireMockAdminClient())
            .Named<IWireMockAdminApi>("my-downstream-service-1-wiremock-admin");
    }

    private static IFutureDockerImage ServiceImage()
    {
        var image = /* some image building... */;
        image.CreateAsync().Wait();
        return image;
    }
}

ScenarioDependencies.cs

public class ScenarioDependencies
{

  [ScenarioDependencies]
  public static void CreateContainerBuilder(ContainerBuilder containerBuilder)
  {
    containerBuilder.AddReqnrollBindings<ScenarioDependencies>();
  }

}

The code annotated with GlobalDependencies runs only if I have a [BeforeTestRun] hook.

After some investigation, I believe it's because of an issue in the BindingRegistryExtensions class. It appears that, at the time of the global container registration, the IBindingRegistry is not ready yet (understandable, as it can't map the step definitions). Without any [BeforeTestRun] hooks, GetBindingTypes returns an empty IEnumerable and GetBindingAssemblies returns an empty IEnumerable too. This ultimately means that GetMethod in ContainerBuilderFinder.cs returns null which causes this condition to trigger:

        protected virtual Func<ContainerBuilder, ContainerBuilder> FindCreateScenarioContainerBuilder(Type attributeType, Type returnType, Func<ContainerBuilder, MethodInfo, ContainerBuilder> invoke)
        {
            var method = GetMethod(attributeType, returnType);

            return method == null
                ? null
                : containerBuilder => invoke(containerBuilder, method);
        }

(e.g., the code thinks there is no [GlobalDependencies]-annotated method!)

Steps to Reproduce

Create a project which uses [GlobalDependencies], uses the Autofac plugin but has no hooks. Add an empty [BeforeTestRun] hook like this:

[Binding]
public class BeforeTestRun
{
    [BeforeTestRun]
    public static void Foo()
    {
    }
}

to make the issue disappear.

I can also set up a repro project if desired!

Link to Repro Project

No response

@michielboekhoff michielboekhoff added the bug Something isn't working label May 10, 2024
@michielboekhoff
Copy link
Author

Okay, so, digging a bit deeper still - it's because we can't access things from the global context inside of our step definitions. In my use-case, have you got any advice on how to achieve this?

@gasparnagy
Copy link
Contributor

@michielboekhoff You can let the ITestRunContext injected, that is the global context. It has a TestRunContainer property, if you need the DI container.

@gasparnagy
Copy link
Contributor

@michielboekhoff I have also run into a similar issue while trying to fix #56, but not exactly yours and I could not reproduce your case.

What I have found was that before processing the [GlobalDependencies] method, the plugin calls the binding registry building here so the binding registry should be ready by that time. Is it possible that the [GlobalDependencies] method in your case was not in the Reqnroll project but in another project?

@gasparnagy
Copy link
Contributor

@michielboekhoff Ignore my last comment. I was able to reproduce the problem. It seems that any hook fixes the problem, not only [BeforeTestRun]... Strange... But at the end my fix to #56 also fixes this one, so I'm not sure I want to fully understand how it happened.

gasparnagy added a commit that referenced this issue May 21, 2024
@gasparnagy
Copy link
Contributor

OK. I understand it now why it was wrong. Basically the IBindingRegistry was half finished at the time the [GlobalDependencies] method was searched for. The step definitions were not processed yet, therefore they cannot report their assemblies. The hooks are immediately processed, so they reported the assembly. This is why the hook was needed. It is fixed now by #139

gasparnagy added a commit that referenced this issue May 21, 2024
* Refactor AutofacPlugin and add unit tests

* Add extension method to add bindings from assembly

* fix duplicate registrations bug

* fix typo

* Fix the fix and also #127

* Add CHANGELOG
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
gasparnagy added a commit that referenced this issue May 22, 2024
…hread-container

* origin/main:
  Extract cucumber expression detection heuristic to an interface
  fix test artifact folder calculation
  Restructure solution folders (#141)
  cleanup CHANGELOG.md
  ignore generated file
  remove generated file
  bump version
  Add Microsoft.Extensions.DependencyInjection integration plugin (#94)
  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)

# Conflicts:
#	CHANGELOG.md
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

2 participants