dadhi / DryIoc

DryIoc is fast, small, full-featured IoC Container for .NET
MIT License
988 stars 122 forks source link

UsingInTestsWithMockingLibrary - generics on constructor error #526

Closed alansingfield closed 1 year ago

alansingfield commented 1 year ago

Hello Dadhi,

https://github.com/dadhi/DryIoc/blob/master/docs/DryIoc.Docs/UsingInTestsWithMockingLibrary.md

I've just updated my DryIoc and the WithAutoMocking() call I use extensively.

For any service with a closed generic as a dependency, we get this error:

Test method MyTestMethod1 threw exception: 
    DryIoc.ContainerException: code: Error.ExpectedSingleDefaultFactory;
    message: Expecting a single default registration but found many:
    (DefaultDynamicKey(0), {FactoryID=545, Reuse=Singleton {Lifespan=1000}}), 
    (DefaultDynamicKey(1), {FactoryID=546, Reuse=Singleton {Lifespan=1000}})
    when resolving TempTest.IGeneric<Int32> as parameter "genInt"
      in resolution root TempTest+Consumer FactoryId=515
      from container without scope
     with .
    Please identify service with key, or metadata, or use Rules.WithFactorySelector to specify single registered factory.

The solution appears to be to bail out if the type specified is an open generic; we get called first with the IGeneric<>, then IGeneric<int> .


        public interface IGeneric<T> { }
        public class Consumer
        {
            public Consumer(IGeneric<int> genInt) { }
        }

        [TestMethod]
        public void MyTestMethod1()
        {
            var container = WithAutoMocking(new Container(), Reuse.Singleton);
            container.Register<Consumer>();

            var consumer = container.Resolve<Consumer>();
        }

        private IContainer WithAutoMocking(IContainer container, IReuse reuse = null) =>
            container.With(rules => rules.WithDynamicRegistration((serviceType, serviceKey) =>
            {
                if(!serviceType.IsAbstract) // Mock interface or abstract class only.
                    return null;

                // Without this check, the error occurs
                if(serviceType.IsOpenGeneric())
                    return null;

                var d = _mockRegistrations.GetOrAdd(serviceType,
                    type => new DynamicRegistration(
                        DelegateFactory.Of(r => Substitute.For(new[] { serviceType }, Empty<object>()), reuse)));

                return new[] { d };
            },
            DynamicRegistrationFlags.Service | DynamicRegistrationFlags.AsFallback));

Is this the correct approach? If so, can you update the docs please?

Thanks,

Alan.

dadhi commented 1 year ago

@alansingfield Thanks for reporting, I will check whats happenning.

dadhi commented 1 year ago

For any service with a closed generic as a dependency, we get this error:

@alansingfield Sorry, forgot about this issue, but this statement is interesting. Will check it sooner.

dadhi commented 1 year ago

I am updating the docs... Your condition is working just fine.