Patch for AutoMockingContainer to resolve against non-public constructors

6 views
Skip to first unread message

Matt Ellis

unread,
Nov 4, 2008, 12:00:55 PM11/4/08
to Rhino.Mocks
Hi folks. I'm currently enjoying the AutoMockingContainer found in
RhinoTesting (although a binary download against the current released
dlls would be nice).

I'm missing support for non-public constructors. I want to write code
that has a default constructor that provides the dependencies to an
internal constructor. In production code, I use the default
constructor. In test code, I use the internal constructor. The current
AutoMockingContainer doesn't know how to handle this usage - it only
works with public constructors.

This patch allows for internal (and even private!) constructors. Hope
the paste works:

Index: Rhino.Testing.Tests/AutoMocking/AddComponentInstanceFixture.cs
===================================================================
--- Rhino.Testing.Tests/AutoMocking/AddComponentInstanceFixture.cs
(revision 1715)
+++ Rhino.Testing.Tests/AutoMocking/AddComponentInstanceFixture.cs
(working copy)
@@ -1,31 +1,34 @@
using MbUnit.Framework;
-using Rhino.Mocks;
-using Rhino.Testing.AutoMocking;
-
-[TestFixture]
-public class AddComponentInstanceFixture
-{
- private AutoMockingContainer container;
- private MockRepository mocks;
-
- public class InstancedComponent
- {
- }
-
- [SetUp]
- public void SetUp()
- {
- mocks = new MockRepository();
- container = new AutoMockingContainer(mocks);
- container.Initialize();
- }
-
-
- [Test]
- public void AddComponentInstance_CanBeRetrieved()
- {
- InstancedComponent component = new InstancedComponent();
- container.Kernel.AddComponentInstance("InstancedComponent", typeof
(InstancedComponent), component);
- Assert.AreEqual(component, container.Get<InstancedComponent>());
- }
+using Rhino.Mocks;
+using Rhino.Testing.AutoMocking;
+
+namespace Rhino.Testing.Tests.AutoMocking
+{
+ [TestFixture]
+ public class AddComponentInstanceFixture
+ {
+ private AutoMockingContainer container;
+ private MockRepository mocks;
+
+ public class InstancedComponent
+ {
+ }
+
+ [SetUp]
+ public void SetUp()
+ {
+ mocks = new MockRepository();
+ container = new AutoMockingContainer(mocks);
+ container.Initialize();
+ }
+
+
+ [Test]
+ public void AddComponentInstance_CanBeRetrieved()
+ {
+ InstancedComponent component = new InstancedComponent();
+
container.Kernel.AddComponentInstance("InstancedComponent",
typeof(InstancedComponent), component);
+ Assert.AreEqual(component,
container.Get<InstancedComponent>());
+ }
+ }
}
\ No newline at end of file
Index: Rhino.Testing.Tests/AutoMocking/AutoMockingContainerTests.cs
===================================================================
--- Rhino.Testing.Tests/AutoMocking/AutoMockingContainerTests.cs
(revision 1715)
+++ Rhino.Testing.Tests/AutoMocking/AutoMockingContainerTests.cs
(working copy)
@@ -27,7 +27,40 @@

}

- [Test]
+ // It might be nice to test this on a class in a separate
assembly...
+ [Test]
+ public void
Create_WillResolveDependenciesOnInternalClassWithInternalConstructor()
+ {
+ InternalComponentBeingConfigured target =
container.Create<InternalComponentBeingConfigured>();
+ Assert.IsNotNull(target.ReallyCoolService, "mocked
ReallyCoolService dependency");
+ Assert.IsNotNull(target.Services, "mocked Services
dependency");
+ }
+
+ [Test]
+ public void
Create_WillResolveDependenciesPreferringInternalConstructor()
+ {
+ ComponentBeingConfiguredWithInternalCtor target =
container.Create<ComponentBeingConfiguredWithInternalCtor>();
+ Assert.IsNotNull(target.ReallyCoolService, "mocked
ReallyCoolService dependency");
+ Assert.IsNotNull(target.Services, "mocked Services
dependency");
+ }
+
+ [Test]
+ public void
Create_WillResolveDependenciesPreferringInternalConstructor_WithResolvePropertiesEnabled()
+ {
+ ComponentBeingConfiguredWithInternalCtor target =
containerThatResolvesProperties.Create<ComponentBeingConfiguredWithInternalCtor>();
+ Assert.IsNotNull(target.ReallyCoolService, "mocked
ReallyCoolService dependency");
+ Assert.IsNotNull(target.Services, "mocked Services
dependency");
+ }
+
+ [Test]
+ public void
Create_WillResolveDependenciesPreferringPrivateConstructor()
+ {
+ ComponentBeingConfiguredWithPrivateCtor target =
container.Create<ComponentBeingConfiguredWithPrivateCtor>();
+ Assert.IsNotNull(target.ReallyCoolService, "mocked
ReallyCoolService dependency");
+ Assert.IsNotNull(target.Services, "mocked Services
dependency");
+ }
+
+ [Test]
public void Create_WillResolveDependenciesAsMocks_ByDefault()
{
ComponentBeingConfigured target =
container.Create<ComponentBeingConfigured>();
Index: Rhino.Testing.Tests/AutoMocking/ComponentBeingConfigured.cs
===================================================================
--- Rhino.Testing.Tests/AutoMocking/ComponentBeingConfigured.cs
(revision 1715)
+++ Rhino.Testing.Tests/AutoMocking/ComponentBeingConfigured.cs
(working copy)
@@ -11,7 +11,6 @@
Services = services;
}

-
public ComponentBeingConfigured(IReallyCoolService
reallyCoolService)
{
ReallyCoolService = reallyCoolService;
Index: Rhino.Testing.Tests/AutoMocking/
ComponentBeingConfiguredWithInternalCtor.cs
===================================================================
--- Rhino.Testing.Tests/AutoMocking/
ComponentBeingConfiguredWithInternalCtor.cs (revision 0)
+++ Rhino.Testing.Tests/AutoMocking/
ComponentBeingConfiguredWithInternalCtor.cs (revision 0)
@@ -0,0 +1,24 @@
+namespace Rhino.Testing.Tests.AutoMocking
+{
+ public class ComponentBeingConfiguredWithInternalCtor
+ {
+ public IReallyCoolService ReallyCoolService;
+ public ICollectionOfServices Services;
+
+ internal
ComponentBeingConfiguredWithInternalCtor(IReallyCoolService
reallyCoolService, ICollectionOfServices services)
+ {
+ ReallyCoolService = reallyCoolService;
+ Services = services;
+ }
+
+ public
ComponentBeingConfiguredWithInternalCtor(IReallyCoolService
reallyCoolService)
+ {
+ ReallyCoolService = reallyCoolService;
+ }
+
+ public void RunDispose()
+ {
+ Services.SomethingToDispose.Dispose();
+ }
+ }
+}
\ No newline at end of file
Index: Rhino.Testing.Tests/AutoMocking/
ComponentBeingConfiguredWithPrivateCtor.cs
===================================================================
--- Rhino.Testing.Tests/AutoMocking/
ComponentBeingConfiguredWithPrivateCtor.cs (revision 0)
+++ Rhino.Testing.Tests/AutoMocking/
ComponentBeingConfiguredWithPrivateCtor.cs (revision 0)
@@ -0,0 +1,23 @@
+namespace Rhino.Testing.Tests.AutoMocking
+{
+ public class ComponentBeingConfiguredWithPrivateCtor
+ {
+ public IReallyCoolService ReallyCoolService;
+ public ICollectionOfServices Services;
+
+ private
ComponentBeingConfiguredWithPrivateCtor(IReallyCoolService
reallyCoolService, ICollectionOfServices services)
+ {
+ ReallyCoolService = reallyCoolService;
+ Services = services;
+ }
+
+ public
ComponentBeingConfiguredWithPrivateCtor(IReallyCoolService
reallyCoolService) : this(reallyCoolService, null)
+ {
+ }
+
+ public void RunDispose()
+ {
+ Services.SomethingToDispose.Dispose();
+ }
+ }
+}
\ No newline at end of file
Index: Rhino.Testing.Tests/AutoMocking/
InternalComponentBeingConfigured.cs
===================================================================
--- Rhino.Testing.Tests/AutoMocking/
InternalComponentBeingConfigured.cs (revision 0)
+++ Rhino.Testing.Tests/AutoMocking/
InternalComponentBeingConfigured.cs (revision 0)
@@ -0,0 +1,19 @@
+namespace Rhino.Testing.Tests.AutoMocking
+{
+ internal class InternalComponentBeingConfigured
+ {
+ public IReallyCoolService ReallyCoolService;
+ public ICollectionOfServices Services;
+
+ internal InternalComponentBeingConfigured(IReallyCoolService
reallyCoolService, ICollectionOfServices services)
+ {
+ ReallyCoolService = reallyCoolService;
+ Services = services;
+ }
+
+ public void RunDispose()
+ {
+ Services.SomethingToDispose.Dispose();
+ }
+ }
+}
\ No newline at end of file
Index: Rhino.Testing.Tests/Rhino.Testing.Tests-vs2008.csproj
===================================================================
--- Rhino.Testing.Tests/Rhino.Testing.Tests-vs2008.csproj (revision
1715)
+++ Rhino.Testing.Tests/Rhino.Testing.Tests-vs2008.csproj (working
copy)
@@ -79,9 +79,12 @@
<Compile Include="AutoMocking\AutoMockingContainerTests.cs" />
<Compile Include="AutoMocking\AutoMockingTests.cs" />
<Compile Include="AutoMocking\ComponentBeingConfigured.cs" />
+ <Compile Include="AutoMocking
\ComponentBeingConfiguredWithInternalCtor.cs" />
+ <Compile Include="AutoMocking
\ComponentBeingConfiguredWithPrivateCtor.cs" />
<Compile Include="AutoMocking\ComponentWithComplexProperty.cs" />
<Compile Include="AutoMocking\DefaultCollectionOfServices.cs" />
<Compile Include="AutoMocking\ICollectionOfServices.cs" />
+ <Compile Include="AutoMocking
\InternalComponentBeingConfigured.cs" />
<Compile Include="AutoMocking\IReallyCoolService.cs" />
<Compile Include="AutoMocking\NeedIKernel.cs" />
<Compile Include="CommentReaderFixture.cs" />
@@ -118,4 +121,4 @@
<Target Name="AfterBuild">
</Target>
-->
-</Project>
+</Project>
\ No newline at end of file
Index: Rhino.Testing/AutoMocking/AutoMockingComponentActivator.cs
===================================================================
--- Rhino.Testing/AutoMocking/AutoMockingComponentActivator.cs
(revision 1715)
+++ Rhino.Testing/AutoMocking/AutoMockingComponentActivator.cs
(working copy)
@@ -1,3 +1,5 @@
+using System;
+using System.Reflection;
using Castle.Core;
using Castle.MicroKernel;
using Castle.MicroKernel.ComponentActivator;
@@ -12,9 +14,14 @@
{
}

- protected override void SetUpProperties(object instance,
CreationContext context)
- {
+ protected override object CreateInstance(CreationContext
context, object[] arguments, System.Type[] signature)
+ {
+ // TODO: Support interceptors + copy "use fast create
instance" logic from DefaultComponentActivator

- }
+ // Support internal and private constructors
+ return Activator.CreateInstance(Model.Implementation,
+
BindingFlags.CreateInstance | BindingFlags.NonPublic |
BindingFlags.Public |
+ BindingFlags.Instance,
null, arguments, null, null);
+ }
}
}
Index: Rhino.Testing/AutoMocking/AutoMockingContainer.cs
===================================================================
--- Rhino.Testing/AutoMocking/AutoMockingContainer.cs (revision 1715)
+++ Rhino.Testing/AutoMocking/AutoMockingContainer.cs (working copy)
@@ -2,10 +2,8 @@
using System.Collections;
using System.Collections.Generic;
using Castle.MicroKernel;
-using Castle.MicroKernel.SubSystems.Naming;
using Castle.Windsor;
using Rhino.Mocks;
-using System.Diagnostics;

namespace Rhino.Testing.AutoMocking
{
@@ -92,6 +90,7 @@
{
Kernel.AddSubSystem(SubSystemConstants.NamingKey,new
AutoMockingNamingSubSystem(this));
Kernel.AddFacility("AutoMockingFacility", new
AutoMockingFacility(this));
+ Kernel.ComponentModelBuilder.AddContributor(new
NonPublicConstructorDependenciesModelInspector());
Kernel.ComponentModelCreated += Kernel_ComponentModelCreated;
}

@@ -99,8 +98,9 @@
{
if (model.CustomComponentActivator!=null)
return;
- if (!ResolveProperties)
- model.CustomComponentActivator =
typeof(AutoMockingComponentActivator);
+ model.CustomComponentActivator = ResolveProperties
+ ? typeof
(AutoMockingComponentActivator)
+ : typeof
(NonPropertyResolvingComponentActivator);
}

private void AddComponentIfMissing<T>()
Index: Rhino.Testing/AutoMocking/
NonPropertyResolvingComponentActivator.cs
===================================================================
--- Rhino.Testing/AutoMocking/
NonPropertyResolvingComponentActivator.cs (revision 0)
+++ Rhino.Testing/AutoMocking/
NonPropertyResolvingComponentActivator.cs (revision 0)
@@ -0,0 +1,18 @@
+using Castle.Core;
+using Castle.MicroKernel;
+
+namespace Rhino.Testing.AutoMocking
+{
+ class NonPropertyResolvingComponentActivator :
AutoMockingComponentActivator
+ {
+ public NonPropertyResolvingComponentActivator(ComponentModel
model, IKernel kernel, ComponentInstanceDelegate onCreation,
ComponentInstanceDelegate onDestruction)
+ : base(model, kernel, onCreation, onDestruction)
+ {
+ }
+
+ protected override void SetUpProperties(object instance,
CreationContext context)
+ {
+ // Do nothing - we're not resolving properties
+ }
+ }
+}
\ No newline at end of file
Index: Rhino.Testing/AutoMocking/
NonPublicConstructorDependenciesModelInspector.cs
===================================================================
--- Rhino.Testing/AutoMocking/
NonPublicConstructorDependenciesModelInspector.cs (revision 0)
+++ Rhino.Testing/AutoMocking/
NonPublicConstructorDependenciesModelInspector.cs (revision 0)
@@ -0,0 +1,18 @@
+using System.Reflection;
+using Castle.MicroKernel;
+using Castle.MicroKernel.ModelBuilder.Inspectors;
+
+namespace Rhino.Testing.AutoMocking
+{
+ internal class NonPublicConstructorDependenciesModelInspector :
ConstructorDependenciesModelInspector
+ {
+ public override void ProcessModel(IKernel kernel,
Castle.Core.ComponentModel model)
+ {
+ base.ProcessModel(kernel, model);
+
+ var constructors =
model.Implementation.GetConstructors(BindingFlags.Instance |
BindingFlags.NonPublic);
+ foreach (var constructorInfo in constructors)
+
model.Constructors.Add(CreateConstructorCandidate(model,
constructorInfo));
+ }
+ }
+}
\ No newline at end of file

Property changes on: Rhino.Testing\AutoMocking
\NonPublicConstructorDependenciesModelInspector.cs
___________________________________________________________________
Added: svn:mergeinfo

Index: Rhino.Testing/Rhino.Testing-v2008.csproj
===================================================================
--- Rhino.Testing/Rhino.Testing-v2008.csproj (revision 1715)
+++ Rhino.Testing/Rhino.Testing-v2008.csproj (working copy)
@@ -81,6 +81,8 @@
<Compile Include="AutoMocking\IAutoMockingRepository.cs" />
<Compile Include="AutoMocking\IMockingStrategy.cs" />
<Compile Include="AutoMocking\NonMockedStrategy.cs" />
+ <Compile Include="AutoMocking
\NonPropertyResolvingComponentActivator.cs" />
+ <Compile Include="AutoMocking
\NonPublicConstructorDependenciesModelInspector.cs" />
<Compile Include="AutoMocking\TypeMarker.cs" />
<Compile Include="CommentReader.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
@@ -115,4 +117,4 @@
<Target Name="AfterBuild">
</Target>
-->
-</Project>
+</Project>
\ No newline at end of file



Cheers
Matt

jasonm...@gmail.com

unread,
Nov 4, 2008, 1:11:12 PM11/4/08
to Rhino.Mocks
did you try using the InternalsVisibleTo assembly attribute? i know
this works for objects, not sure about ctors. this would allow you to
expose the internal ctors for testing, but not for production.
upload the patch to the files section of the group. then one of the
committers can download and apply the patch.

Matt Ellis

unread,
Nov 5, 2008, 4:30:14 AM11/5/08
to Rhino.Mocks
It doesn't work for this, because Windsor is trying to create
everything via reflection, and the flags it's passing around are for
only public constructors. InternalsVisibleTo couldn't help here,
either, because my test method and the class with internal
constructors are in the same assembly (Rhino.Testing.Tests.dll)

The patch overrides Windsor's default behaviour to add all public and
non-public constructors to the list of constructor candidates, and it
provides a new activator that will call Activator.CreateInstance with
both public and non-public flags.

Cheers
Matt

On Nov 4, 6:11 pm, "jasonmeck...@gmail.com" <jasonmeck...@gmail.com>
wrote:
> > Create_WillResolveDependenciesPreferringInternalConstructor_WithResolveProp­ertiesEnabled()
> > +        {
> > +            ComponentBeingConfiguredWithInternalCtor target =
> > containerThatResolvesProperties.Create<ComponentBeingConfiguredWithInternal­Ctor>();
> ...
>
> read more »- Hide quoted text -
>
> - Show quoted text -

Matt Ellis

unread,
Nov 13, 2008, 4:14:12 AM11/13/08
to Rhino.Mocks
Hi folks.

Anyone had a chance to look at this? Any comments?

Matt

Ayende Rahien

unread,
Nov 13, 2008, 4:59:46 PM11/13/08
to Rhino...@googlegroups.com
Can you send this as a zip file?

Matt Ellis

unread,
Nov 14, 2008, 6:53:11 PM11/14/08
to Rhino.Mocks
Sure. What do you a want a zip of? The source to the Rhino.Testing
solution? And where do you want me to send it?

Cheers
Matt

On Nov 13, 9:59 pm, "Ayende Rahien" <aye...@ayende.com> wrote:
> Can you send this as a zip file?
>

Ayende Rahien

unread,
Nov 14, 2008, 7:02:19 PM11/14/08
to Rhino...@googlegroups.com
A zip of the patch

Matt Ellis

unread,
Nov 25, 2008, 9:09:47 AM11/25/08
to Rhino.Mocks
Hi Ayende. Did you receive the zip file?
> > > > > This patch allows for internal (and even private!) constructors.- Hide quoted text -
Reply all
Reply to author
Forward
0 new messages