Following on from yesterdays post about creating two different types from a single interface - I have my answer. In fact I've got two... one's dirty, one's clean. Thanks to Hadi for the clean answer, which is what I've gone with.
Dirty Answer
Those of a sensitive disposition may want to look away now, or at least scroll to the next paragraph, because this isn't going to be pretty. I dug into the Rhino Mock assembly looking for the point where, maybe, I'd be able to switch the default behaviour to want I wanted. In my quest I found this method on MockRepository:
1: protected virtual ProxyGenerator GetProxyGenerator(Type type)
2: {
3: if (!generatorMap.ContainsKey(type))
4: {
5: generatorMap[type] = new ProxyGenerator();
6: }
7: return generatorMap[type];
8: }
"Oh yes!" thinks I. All I have to do is inherit MockRepository as MultiTypeMockRepository, override GetProxyGenerator and then always return a new ProxyGenerator. Alas, this was not to be, somehow the people behind Rhino have managed to expose an internal type (ProxyGenerator) as a return value on a protected method of a public class. For a brief moment I contemplated grabbing the Rhino source and rolling my own, but the overhead for a few simple tests wasn't worth it. Finding this method had exposed to me the cache of ProxyGenerators that Rhino are using. My next line of thought was to use reflection to tinker with the innards of MockRepository (told you this was the dirty answer):
1: public static class MyMockRepository
2: {
3: public static void FlushProxyGenerators()
4: {
5: Type mrType = typeof(MockRepository);
6:
7: FieldInfo generatorMapField = mrType.GetField("generatorMap", BindingFlags.NonPublic | BindingFlags.Static);
8:
9: object generatorMap = generatorMapField.GetValue(null);
10:
11: lock (generatorMap)
12: {
13: Type generatorMapType = generatorMap.GetType();
14: PropertyInfo keys = generatorMapType.GetProperty("Keys");
15: MethodInfo remove = generatorMapType.GetMethod("Remove");
16:
17: List<Type> types = new List<Type>(keys.GetValue(generatorMap, null) as IEnumerable<Type>);
18:
19: foreach (Type t in types) remove.Invoke(generatorMap, new object[] { t });
20: }
21: }
22: }
Guess what? It worked. Although my unit test code now had a rather odd call to FlushProxyGenerators in between the creation of the two outbound classes. That and I'd broken pretty much every rule in the book... dang.
Clean Answer
Hadi had suggested that I create a couple of stub concrete classes from my interface, set the SendMessage method as virtual and then mock from them. The resultant code looks thus:
1: public class MockEmailService : IOutboundService
2: {
3: #region IOutboundService Members
4:
5: public virtual void SendMessage(Dictionary<string, string> data, IEnumerable<IServicePolicy> policies)
6: {
7: throw new NotImplementedException();
8: }
9:
10: #endregion
11: }
1: public class MockFtpService : IOutboundService, IInboundService
2: {
3: #region IOutboundService Members
4:
5: public virtual void SendMessage(Dictionary<string, string> data, IEnumerable<IServicePolicy> policies)
6: {
7: throw new NotImplementedException();
8: }
9:
10: #endregion
11:
12: #region IInboundService Members
13:
14: public event EventHandler<OrderReceivedEventArgs> OrderReceived;
15:
16: public event EventHandler<OrderNotificationReceivedEventArgs> OrderNotificationReceived;
17:
18: public event EventHandler<OrderAssociationReceivedEventArgs> OrderAssociationReceived;
19:
20: public event EventHandler<OrderActivationReceivedEventArgs> OrderActivationReceived;
21:
22: #endregion
23: }
1: [TestMethod]
2: public void SendMessage_TwoOutbound_RoutedToTwoCorrectly()
3: {
4: IOutboundService outbound1 = _mockRepository.StrictMock<MockEmailService>();
5: IOutboundService outbound2 = _mockRepository.StrictMock<MockFtpService>();
6: ProcessOutboundRouter router = new ProcessOutboundRouter();
7: router.Add(outbound1);
8: router.Add(outbound2);
9:
10: IServicePolicy policy1 = _mockRepository.StrictMock<IServicePolicy>();
11: IServicePolicy policy2 = _mockRepository.StrictMock<IServicePolicy>();
12:
13: Dictionary<string, string> data = new Dictionary<string, string>();
14:
15: Expect.Call(policy1.ServiceType).Return(outbound1.GetType());
16: Expect.Call(delegate { outbound1.SendMessage(data, new IServicePolicy[] { policy1 }); });
17: Expect.Call(policy2.ServiceType).Return(outbound2.GetType());
18: Expect.Call(delegate { outbound2.SendMessage(data, new IServicePolicy[] { policy2 }); });
19:
20: _mockRepository.ReplayAll();
21: router.SendMessage(data, new IServicePolicy[] { policy1, policy2 });
22: _mockRepository.VerifyAll();
23: }
Note : I've changed the name of some of the interfaces.
Much neater in the test, and without the pangs of guilt I get when I do something really nasty.