How to unit test open generic decorator chains in SimpleInjector 2.6.1+

此生再无相见时 提交于 2019-12-12 15:28:16

问题


Given the following open generic deocrator chain using SimpleInjector:

container.RegisterManyForOpenGeneric(typeof(IHandleQuery<,>), assemblies);
container.RegisterDecorator(
    typeof(IHandleQuery<,>),
    typeof(ValidateQueryDecorator<,>)
);
container.RegisterSingleDecorator(
    typeof(IHandleQuery<,>),
    typeof(QueryLifetimeScopeDecorator<,>)
);
container.RegisterSingleDecorator(
    typeof(IHandleQuery<,>),
    typeof(QueryNotNullDecorator<,>)
);

With SimpleInjector 2.4.0, I was able to unit test this to assert the decoration chain using the following code:

[Fact]
public void RegistersIHandleQuery_UsingOpenGenerics_WithDecorationChain()
{
    var instance = Container
        .GetInstance<IHandleQuery<FakeQueryWithoutValidator, string>>();
    InstanceProducer registration = Container.GetRegistration(
        typeof(IHandleQuery<FakeQueryWithoutValidator, string>));

    instance.ShouldNotBeNull();
    registration.Registration.ImplementationType
        .ShouldEqual(typeof(HandleFakeQueryWithoutValidator));
    registration.Registration.Lifestyle.ShouldEqual(Lifestyle.Transient);
    var decoratorChain = registration.GetRelationships()
        .Select(x => new
        {
            x.ImplementationType,
            x.Lifestyle,
        })
        .Reverse().Distinct().ToArray();
    decoratorChain.Length.ShouldEqual(3);
    decoratorChain[0].ImplementationType.ShouldEqual(
        typeof(QueryNotNullDecorator<FakeQueryWithoutValidator, string>));
    decoratorChain[0].Lifestyle.ShouldEqual(Lifestyle.Singleton);
    decoratorChain[1].ImplementationType.ShouldEqual(
        typeof(QueryLifetimeScopeDecorator<FakeQueryWithoutValidator, string>));
    decoratorChain[1].Lifestyle.ShouldEqual(Lifestyle.Singleton);
    decoratorChain[2].ImplementationType.ShouldEqual(
        typeof(ValidateQueryDecorator<FakeQueryWithoutValidator, string>));
    decoratorChain[2].Lifestyle.ShouldEqual(Lifestyle.Transient);
}

After updating to SimpleInjector 2.6.1, this unit test fails. It seems that InstanceProducer.Registration.ImplementationType now returns the first decoration handler rather than the decorated handler (meaning, it returns typeof(QueryNotNullDecorator<HandleFakeQueryWithoutValidator,string>) instead of typeof(HandleFakeQueryWithoutValidator).

Also, InstanceProducer.GetRelationships() no longer returns all of the decorators in the chain. it also only returns the first decorator.

Is this a bug and, if not, how can we unit test open generic decorator chains using SimpleInjector 2.6.1+?


回答1:


The detail available for the dependency graph has been greatly improved in 2.6. You can achieve the same thing with this code:

[Fact]
public void RegistersIHandleQuery_UsingOpenGenerics_WithDecorationChain()
{
    var container = this.ContainerFactory();

    var instance = container
        .GetInstance<IHandleQuery<FakeQueryWithoutValidator, string>>();

    var registration = (
        from currentRegistration in container.GetCurrentRegistrations()
        where currentRegistration.ServiceType ==
            typeof(IHandleQuery<FakeQueryWithoutValidator, string>)
        select currentRegistration.Registration)
        .Single();
    Assert.Equal(
        typeof(QueryNotNullDecorator<FakeQueryWithoutValidator, string>), 
        registration.ImplementationType);
    Assert.Equal(Lifestyle.Singleton, registration.Lifestyle);

    registration = registration.GetRelationships().Single().Dependency.Registration;
    Assert.Equal(
        typeof(QueryLifetimeScopeDecorator<FakeQueryWithoutValidator, string>), 
        registration.ImplementationType);
    Assert.Equal(Lifestyle.Singleton, registration.Lifestyle);

    registration = registration.GetRelationships().Single().Dependency.Registration;
    Assert.Equal(
        typeof(ValidateQueryDecorator<FakeQueryWithoutValidator, string>), 
        registration.ImplementationType);
    Assert.Equal(Lifestyle.Transient, registration.Lifestyle);
}

You can find more information here

Please note: I think you have a captive dependency - you have a transient handler inside of a singleton decorator ...

[Fact]
public void Container_Always_ContainsNoDiagnosticWarnings()
{
    var container = this.ContainerFactory();

    container.Verify();

    var results = Analyzer.Analyze(container);

    Assert.False(results.Any());
}



回答2:


Qujck is right. We greatly improved the way registrations and KnownDependency graphs are built, especially to improve diagnostics and to make it easier for users to query the registrations. So this is not a bug; this is a breaking change. We however didn't expect anyone to be affected by this and that's why we made the change in a minor release. I'm sorry you had to stumble upon this change, but at least it's just test code that breaks.

In previous versions the graph of KnownDependency objects was flattened when decorators where added. This made querying and visualising the object graph hard. In v2.6, from perspective of the diagnostic API, it is as if a decorator is a 'real' registration. This means that the InstanceProducer and Registration objects now show the real type that is returned and its lifestyle.

Much clearer, but a breaking change in the diagnostic API.



来源:https://stackoverflow.com/questions/27668782/how-to-unit-test-open-generic-decorator-chains-in-simpleinjector-2-6-1

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!