public void Dispose_WithAsyncDisposable_ThrowsException() { // Arrange var container = ContainerFactory.New(); container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle(); container.Register <AsyncDisposable>(Lifestyle.Scoped); var scope = AsyncScopedLifestyle.BeginScope(container); var plugin = container.GetInstance <AsyncDisposable>(); // Act Action action = () => scope.Dispose(); // Assert AssertThat.ThrowsWithExceptionMessageContains <InvalidOperationException>( "AsyncDisposable only implements IAsyncDisposable, but not IDisposable. Make sure to call " + "Scope.DisposeScopeAsync() instead of Scope.Dispose().", action); }
public async Task DisposeScopeAsync_WithSyncAndAsyncDisposableScopedInstanceDelegateRegistration_DisposesThatInstanceOnlyAsynchronously() { // Arrange var container = ContainerFactory.New(); container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle(); container.Register <IFoo>(() => new SyncAsyncDisposable(), Lifestyle.Scoped); var scope = AsyncScopedLifestyle.BeginScope(container); var plugin = container.GetInstance <IFoo>() as SyncAsyncDisposable; // Act await scope.DisposeScopeAsync(); // Assert Assert.IsTrue(plugin.AsyncDisposed); Assert.IsFalse(plugin.SyncDisposed, "In case both interfaces are implemented, only DisposeAsync should be called. " + "The C# compiler acts this way as well."); }
public void Dispose_WithSyncAsyncDisposableScopedRegistration_DisposesThatInstance() { // Arrange var container = ContainerFactory.New(); container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle(); container.Register <SyncAsyncDisposable>(Lifestyle.Scoped); var scope = AsyncScopedLifestyle.BeginScope(container); var plugin = container.GetInstance <SyncAsyncDisposable>(); // Act // Because SyncAsyncDisposable implements IDisposable, the sync dispose call should succeed. scope.Dispose(); // Assert Assert.IsTrue(plugin.SyncDisposed); Assert.IsFalse(plugin.AsyncDisposed, "DisposeAsync was called, but this involves blocking which could cause a deadlock. A no-no."); }
public void GetAllInstances_OnUnregisteredType_TriggersUnregisteredTypeResolution() { // Arrange bool resolveUnregisteredTypeWasTriggered = false; var container = ContainerFactory.New(); container.ResolveUnregisteredType += (s, e) => { if (e.UnregisteredServiceType == typeof(IEnumerable <Exception>)) { resolveUnregisteredTypeWasTriggered = true; } }; // Act Action action = () => container.GetAllInstances <Exception>(); // Assert AssertThat.Throws <ActivationException>(action); Assert.IsTrue(resolveUnregisteredTypeWasTriggered); }
public void GetInstance_OnUnregisteredCollection_TriggersUnregisteredTypeResolution() { // Arrange bool resolveUnregisteredTypeWasTriggered = false; var container = ContainerFactory.New(); container.ResolveUnregisteredType += (s, e) => { if (e.UnregisteredServiceType == typeof(IEnumerable <Exception>)) { resolveUnregisteredTypeWasTriggered = true; e.Register(() => Enumerable.Empty <Exception>()); } }; // Act container.GetInstance <IEnumerable <Exception> >(); // Assert Assert.IsTrue(resolveUnregisteredTypeWasTriggered); }
public void GetInstance_RequestingOnRootTypeDependingIndirectlyOnItselfThroughDelegateRegistration_Throws() { // Arrange var container = ContainerFactory.New(); container.Register <ComponentDependingOn <IOne> >(); container.Register <IOne>(() => new One(container.GetInstance <ITwo>())); container.Register <ITwo>(() => new Two(container.GetInstance <IOne>())); try { // Act container.GetInstance <ComponentDependingOn <IOne> >(); // Assert Assert.Fail("An exception was expected, because A depends indirectly on itself."); } catch (ActivationException) { // This exception is expected. } }
public void GetInstance_EventRegisteredForNonRootTypeThatThrowsException_ThrowsAnDescriptiveException() { // Arrange var container = ContainerFactory.New(); container.Register <UserServiceBase, RealUserService>(); container.ResolveUnregisteredType += (s, e) => { e.Register(() => { throw new Exception(); }); }; // Act Action action = () => container.GetInstance <UserServiceBase>(); // Assert AssertThat.ThrowsWithExceptionMessageContains <Exception>( "The delegate that was registered for service type IUserRepository using the " + "UnregisteredTypeEventArgs.Register(Func<object>) method threw an exception", action, "Exception message was not descriptive."); }
public void GetInstance_RequestingSingletonTypeDependingIndirectlyOnItselfViaSingletonType_Throws() { // Arrange var container = ContainerFactory.New(); // Note: A depends on B which depends on A. container.Register <A>(Lifestyle.Singleton); container.Register <B>(Lifestyle.Singleton); try { // Act container.GetInstance <A>(); // Assert Assert.Fail("An exception was expected, because A depends indirectly on itself."); } catch (ActivationException) { // This exception is expected. } }
public void GetInstance_RequestingSingletonTypeDependingIndirectlyOnItselfThroughInterfaces_Throws() { // Arrange var container = ContainerFactory.New(); // One depends on ITwo and Two depends on IOne. container.Register <IOne, One>(Lifestyle.Singleton); container.Register <ITwo, Two>(Lifestyle.Singleton); try { // Act container.GetInstance <IOne>(); // Assert Assert.Fail("An exception was expected, because A depends indirectly on itself."); } catch (ActivationException) { // This exception is expected. } }
public void GetInstance_MultipleRegisteredInitializers_RunsInitializersInOrderOfRegistration() { // Arrange int index = 1; int initializer1Index = 0; int initializer2Index = 0; int initializer3Index = 0; var container = ContainerFactory.New(); container.RegisterInitializer <ICommand>(c => { initializer1Index = index++; }); container.RegisterInitializer <ConcreteCommand>(c => { initializer3Index = index++; }); container.RegisterInitializer <CommandBase>(c => { initializer2Index = index++; }); // Act container.GetInstance <ConcreteCommand>(); // Assert Assert.AreEqual(1, initializer1Index, "ICommand initializer did not run in expected order."); Assert.AreEqual(2, initializer3Index, "ConcreteCommand initializer did not run in expected order."); Assert.AreEqual(3, initializer2Index, "CommandBase initializer did not run in expected order."); }
public void InitializeInstance_WithPropertyInjection_InjectsProperty() { // Arrange var instanceToInitialize = new ServiceWithProperty <RealTimeProvider>(); Assert.IsNull(instanceToInitialize.Dependency, "Test setup failed."); var container = ContainerFactory.New(); container.Options.PropertySelectionBehavior = new PredicatePropertySelectionBehavior { Predicate = property => property.Name == "Dependency" }; var registration = container.GetRegistration(instanceToInitialize.GetType()).Registration; // Act registration.InitializeInstance(instanceToInitialize); // Assert Assert.IsNotNull(instanceToInitialize.Dependency); }
public void Register_AbstractTypeWithSinglePublicConstructor_ThrowsExpectedException() { // Arrange string expectedMessage = @" The given type RegisterConcreteTests.AbstractTypeWithSinglePublicConstructor is not a concrete type. Please use one of the other overloads to register this type. ".TrimInside(); var container = ContainerFactory.New(); try { // Act container.Register<AbstractTypeWithSinglePublicConstructor>(); Assert.Fail("The abstract type was not expected to be registered successfully."); } catch (ArgumentException ex) { AssertThat.ExceptionMessageContains(expectedMessage, ex); } }
public void GetInstance_SubTypeRegisteredWithFuncReturningNull_ThrowsExpectedException() { // Arrange var container = ContainerFactory.New(); container.Register <IUserRepository>(() => null); try { // Act container.GetInstance <RealUserService>(); // Assert Assert.Fail("Exception expected."); } catch (ActivationException ex) { Assert.IsTrue(ex.Message.Contains( "The registered delegate for type IUserRepository returned null."), "Actual: " + ex.Message); } }
public void GetInstance_LambdaThatCallsBackIntoContainerExecutedFromScopeResolve_ResolvesTheInstanceAsScoped() { // Arrange var container = ContainerFactory.New(); container.Options.DefaultScopedLifestyle = ScopedLifestyle.Flowing; // Calling back into the container to get a scoped instance, from within an instanceCreator lambda, // should work, in case the the root object is resolved from a scope. container.Register <ILogger>(() => container.GetInstance <NullLogger>()); container.Register <NullLogger>(Lifestyle.Scoped); container.Register <ServiceDependingOn <ILogger> >(); var scope = new Scope(container); // Act var s1 = scope.GetInstance <ServiceDependingOn <ILogger> >(); var s2 = scope.GetInstance <ServiceDependingOn <ILogger> >(); // Assert Assert.AreSame(s1.Dependency, s2.Dependency, "Logger was expected to be scoped."); }
public void RegisterTypes_SuppliedWithOpenGenericType_FailsWithExpectedException() { // Arrange var container = ContainerFactory.New(); Type[] types = new[] { typeof(GenericHandler <>) }; // Act Action action = () => container.Register(typeof(IBatchCommandHandler <>), types); // Assert AssertThat.ThrowsWithParamName("implementationTypes", action); AssertThat.ThrowsWithExceptionMessageContains <ArgumentException>(@" The supplied list of types contains an open-generic type, but this method is unable to handle open-generic implementations—it can only map a single implementation to closed-generic service types. You must register this open-generic type separately using the Register(Type, Type) overload. Alternatively, try using Container.Collection.Register instead, if you expect to have multiple implementations per closed-generic service type and want to inject a collection of them into consumers." .TrimInside(), action); }
public void RegisterOpenGeneric_PredicateContext_ImplementationTypeIsClosedImplentation() { bool called = false; // Arrange var container = ContainerFactory.New(); container.RegisterConditional(typeof(IOpenGenericWithPredicate <>), typeof(OpenGenericWithPredicate1 <>), Lifestyle.Transient, c => { Assert.IsFalse(c.ImplementationType.ContainsGenericParameter(), "ImplementationType should be a closed type"); called = true; return(true); }); // Act var result = container.GetInstance <IOpenGenericWithPredicate <int> >(); // Assert Assert.IsTrue(called, "Predicate was not called"); }
public void GetInstance_GenericConditionalRegistrationWithFallbackBehaviorRegisteredBeforeClosed_Throws() { // Arrange var container = ContainerFactory.New(); // Conditional fallback before the closed registration. container.RegisterConditional(typeof(IGeneric <>), typeof(GenericType <>), c => !c.Handled); container.Register <IGeneric <int>, IntGenericType>(); // Act Action action = () => container.GetInstance <IGeneric <int> >(); // Assert AssertThat.ThrowsWithExceptionMessageContains <ActivationException>( "Multiple applicable registrations found for IGeneric<Int32>", action); AssertThat.ThrowsWithExceptionMessageContains <ActivationException>( "make the fallback registration last and check the Handled property in the predicate", action); }
public void GetInstance_MultipleApplicableConditionalNonGenericRegistrations_ThrowsExpectedException() { // Arrange var container = ContainerFactory.New(); container.RegisterConditional(typeof(ILogger), typeof(NullLogger), Lifestyle.Singleton, c => true); container.RegisterConditional(typeof(ILogger), typeof(ConsoleLogger), Lifestyle.Singleton, c => true); // Act Action action = () => container.GetInstance <ServiceWithDependency <ILogger> >(); // Assert AssertThat.ThrowsWithExceptionMessageContains <ActivationException>(@" Multiple applicable registrations found for ILogger. The applicable registrations are (1) the conditional registration for ILogger using NullLogger and (2) the conditional registration for ILogger using ConsoleLogger. If your goal is to make one registration a fallback in case another registration is not applicable, make the fallback registration last and check the Handled property in the predicate." .TrimInside(), action); }
public void RegisterConditionalGeneric_ForConstraintTypeAfterAnUnconditionalConstraintRegistrationForTheSameImplementationTypeHasBeenMade_ThrowsAnExpressiveException() { // Arrange var container = ContainerFactory.New(); container.Register(typeof(IGeneric <>), typeof(GenericClassType <>)); // Act // Although we skip checks for types with type constraints, these two registrations use the same // implementation type and this will always cause overlap. Action action = () => container.RegisterConditional(typeof(IGeneric <>), typeof(GenericClassType <>), c => true); // Assert AssertThat.ThrowsWithExceptionMessageContains <InvalidOperationException>(@" There is already a registration for IGeneric<T> (with implementation GenericClassType<TClass>) that overlaps with the registration for GenericClassType<TClass> that you are trying to make. This new registration would cause ambiguity, because both registrations would be used for the same closed service types. Either remove one of the registrations or make them both conditional." .TrimInside(), action); }
public void GetInstance_DelegateReturningNullRegisteredUsingRegisterSingleByFuncOfNonRootType_ThrowsActivationExceptionWithExpectedExceptionMessage() { // Arrange var container = ContainerFactory.New(); container.RegisterSingle <IUserRepository>(() => null); try { // Act container.GetInstance <RealUserService>(); // Assert Assert.Fail("The GetInstance method was expected to fail, because of the faulty registration."); } catch (ActivationException ex) { string expectedMessage = "The registered delegate for type IUserRepository returned null."; AssertThat.ExceptionMessageContains(expectedMessage, ex); } }
public void GetInstance_UnregisteredConcreteTypeWithInterceptor_CallsEventOnce() { // Arrange int expectedCallCount = 1; int actualCallCount = 0; var container = ContainerFactory.New(); container.ExpressionBuilt += (sender, e) => { if (e.RegisteredServiceType == typeof(SqlUserRepository)) { actualCallCount++; } }; // Act container.GetInstance <SqlUserRepository>(); // Assert Assert.AreEqual(expectedCallCount, actualCallCount); }
public void GetInstance_ResolvingScopedDependencyDirectlyFromScope_ResolvesTheInstanceAsScoped() { // Arrange var container = ContainerFactory.New(); // We need a 'dummy' scoped lifestyle to be able to use Lifestyle.Scoped container.Options.DefaultScopedLifestyle = ScopedLifestyle.Flowing; container.Register <ILogger, NullLogger>(Lifestyle.Scoped); var scope1 = new Scope(container); var scope2 = new Scope(container); // Act var s1 = scope1.GetInstance <ServiceDependingOn <ILogger> >(); var s2 = scope1.GetInstance <ServiceDependingOn <ILogger> >(); var s3 = scope2.GetInstance <ServiceDependingOn <ILogger> >(); // Assert Assert.AreSame(s1.Dependency, s2.Dependency, "Logger was expected to be scoped but was transient."); Assert.AreNotSame(s3.Dependency, s2.Dependency, "Logger was expected to be scoped but was singleton."); }
public void GetInstance_CalledForDecoratedUncontrolledCollection_CallsEventForBothTheInstanceAndTheDecorator() { // Arrange var actualContexts = new List <InstanceInitializationData>(); var container = ContainerFactory.New(); // Container uncontrolled collection IEnumerable <ICommandHandler <RealCommand> > handlers = new ICommandHandler <RealCommand>[] { new StubCommandHandler(), }; container.Collection.Register <ICommandHandler <RealCommand> >(handlers); container.RegisterInitializer(actualContexts.Add, TruePredicate); container.RegisterDecorator(typeof(ICommandHandler <>), typeof(RealCommandHandlerDecorator)); // Act var producer = container.GetRegistration(typeof(IEnumerable <ICommandHandler <RealCommand> >)); var decorator = container.GetAllInstances <ICommandHandler <RealCommand> >().Single() as RealCommandHandlerDecorator; // Assert Assert.AreEqual(2, actualContexts.Count, "Two event args were expected."); Assert.AreSame(producer.Registration, actualContexts.First().Context.Registration); Assert.AreEqual( typeof(IEnumerable <ICommandHandler <RealCommand> >), actualContexts.First().Context.Registration.ImplementationType); Assert.AreEqual( typeof(RealCommandHandlerDecorator), actualContexts.Second().Context.Registration.ImplementationType); }
public void GetInstance_ExpressionBuildingChangedExpressionInAnIncompatibleWay_ThrowsExpectedException() { // Arrange var container = ContainerFactory.New(); container.Register <IUserRepository, SqlUserRepository>(Lifestyle.Singleton); // Act container.ExpressionBuilding += (s, e) => e.Expression = Expression.Constant("some string", typeof(string)); Action action = () => container.GetInstance(typeof(IUserRepository)); // Assert AssertThat.ThrowsWithExceptionMessageContains <ActivationException>(@" You are trying to set the ExpressionBuildingEventArgs.Expression property with an Expression instance that has a type of String. The expression type however should be a SqlUserRepository (or a sub type). You can't change the type of the expression using the ExpressionBuilding event. If you need to change the implementation, please use the ExpressionBuilt event instead." .TrimInside(), action); }
public void GetInstance_CalledSimultaneouslyToRequestTheSameType_ShouldNotTriggerTheRecursionProtection() { // Arrange var container = ContainerFactory.New(); container.Register <IUserRepository>(() => { // We wait a bit here to make sure all three threads run this method simultaneously. Thread.Sleep(200); return(new SqlUserRepository()); }); // Act var thread2 = ThreadWrapper.StartNew(() => container.GetInstance <IUserRepository>()); var thread3 = ThreadWrapper.StartNew(() => container.GetInstance <IUserRepository>()); // Also run on this thread. container.GetInstance <IUserRepository>(); // Assert Assert_FinishedWithoutExceptions(thread2); Assert_FinishedWithoutExceptions(thread3); }
public void GetRegistration_WhenTheRegistrationIsUnknownButCanBeCreatedUsingUnregTypeRes_DoesNotLockTheContainer() { // Arrange var container = ContainerFactory.New(); // Not sure whether it would be wise to always lock the container when an unregistered event handler is fired. container.ResolveUnregisteredType += (s, e) => { e.Register(Lifestyle.Singleton.CreateRegistration <RealTimeProvider>(container)); }; // Act var prod = container.GetRegistration(typeof(ITimeProvider)); Assert.IsNotNull(prod, "Test setup failed."); // Arrange Assert.IsTrue(container.IsLocked, @" Whenever a not explicitly made registration can be returned using unregistered type resolution, the container needs to be locked, changing the container might invalidate the registration. For instance, adding unregistered type resolution events later, might cause a different registration to be returned when GetRegistration is called again."); }
public void Validate_InValidRegisterSingleByFuncRegistration_ThrowsExpectedExceptionMessage() { // Arrange string expectedMessage = "The registered delegate for type IUserRepository returned null"; var container = ContainerFactory.New(); Func <IUserRepository> invalidDelegate = () => null; container.RegisterSingle <IUserRepository>(invalidDelegate); try { // Act container.Verify(); // Arrange Assert.Fail("Exception expected."); } catch (InvalidOperationException ex) { AssertThat.StringContains(expectedMessage, ex.Message); } }
public void Verify_ResolvingACollectionOfSingletonsBeforeAndAfterCallingVerify_ShouldStillYieldTheSameInstance() { // Arrange var container = ContainerFactory.New(); container.Collection.Register(typeof(IEventHandler <>), new Type[] { typeof(StructEventHandler), }); container.Collection.Append(typeof(IEventHandler <>), Lifestyle.Singleton.CreateRegistration <AuditableEventEventHandler>(container)); var handler = container.GetAllInstances <IEventHandler <AuditableEvent> >().Single(); container.Verify(); // Act var handler2 = container.GetAllInstances <IEventHandler <AuditableEvent> >().Single(); // Assert Assert.AreSame(handler, handler2); }
public void Dispose_MultipleDisposableSingletons_DisposesThemInOppositeOrderOfCreation() { // Arrange var instances = new List <DisposableService>(); var container = ContainerFactory.New(); container.Register <IService, DisposableService>(Lifestyle.Singleton); container.Register <DisposableService>(Lifestyle.Singleton); var instance1 = container.GetInstance <DisposableService>(); var instance2 = container.GetInstance <IService>() as DisposableService; instance1.Disposing += instances.Add; instance2.Disposing += instances.Add; // Act container.Dispose(); // Assert Assert.AreSame(instance2, instances[0], "Instances are expected to be disposed in opposite order."); Assert.AreSame(instance1, instances[1], "Instances are expected to be disposed in opposite order."); }
public void GetInstance_WithUnregisteredType_InvokesEvent() { // Arrange bool eventCalled = false; var container = ContainerFactory.New(); container.ResolveUnregisteredType += (s, e) => { eventCalled = true; }; try { // Act container.GetInstance <IUserRepository>(); // Assert Assert.Fail("Exception was expected."); } catch (ActivationException) { Assert.IsTrue(eventCalled, "Before throwing an exception, the container must try to resolve " + "a missing type by calling the ResolveUnregisteredType event."); } }