WCF, Mocking and IoC: Oh MY!
Introduction:
I need to consume a service provided by a third party, and I would really not like to do production debugging on it. So I will use unit tests to provide me with an assurance that I am handling all the assumption correctly. Like I said, this service is provided by a 3rd party, so I don't care how it is implemented. I do care how easy it is for me to cunsume and use this service, and I like to learn new stuff every now and then, so I decided to use WCF to do this interaction.
Here is the service documentation:
I have a set of tools that I like to work with, and I don't see a reason why using WCF would change that, therefor, I am probably going to have to do some work to integrate WCF into the set of tools that I can use. Let us start by defining the entities we are going to work with:
For simplicity sake, I am going to ignore a lot of other interesting properties (shipping address, billing, etc). My recommended way of operation is Test First, so let us try writing the test code for the OrderController:
[TestFixture]
public class OrderControllerFixture
{
[Test]
public void CanCreateController()
{
IOrderService orderService = null;
OrderController controller = new OrderController(orderService);
}
}
I don't have anything else, but I think that I would need an order service and an order controller. The distinction is important. The IOrderService is concerned mainly with the "how" - in this case, submitting an order for processing. The OrderController is concerned mainly with the "why", it takes a request (usually from a user) and turn it into an action with a business meaning.
In this case, I specify that OrderController takes a IOrderService in its constructor. I won't bore you with the details of how I build a class and constructor. Now, I can test write the next test:
[Test]
public void WillSubmitOrderToService()
{
MockRepository mocks = new MockRepository();
IOrderService orderService = mocks.CreateMock<IOrderService>();
orderService.SubmitOrder(null);
LastCall.IgnoreArguments();
mocks.ReplayAll();
OrderController controller = new OrderController(orderService);
int orderId = 5;
controller.SubmitOrder(orderId);
mocks.VerifyAll();
}
Couple of things to note here, I am creating a mock of IOrderService, and then I setup an expectation that its SubmitOrder will be called (and I don't care about the arguments). I then create the controller with the mocked order service, and submit an order.
I had to think for a couple of minutes when I wrote the test, what should the controller's SubmitOrder take? I am going to use this over the web, and probably this will be used as the last stage of a wizard. This indicate to me that an integer (primary key) is a good idea, since it is something that can easily be passed from the request/view. This also means that I need some sort of persistent storage, likely a database. For simplicity sake, I am going to hand-wave this, assume that I am using Active Record and that I have already set the data up correctly.
The next decision is what should the order service SubmitOrder should accept. My business requirements are such that it should accept the entire Order object (and all its assoicated objects, such as order lines, products, etc), because the service would also need to print a reciept of the order as well as bill it.
Running the test now will fail, because the SubmitOrder on the controller it empty, I added an simple implementation that made it work:
[Transient]
public
{
public void SubmitOrder(int orderId)
{
orderService.SubmitOrder(null);
}
}
The job of the next test is to assert that we send a valid order:
[Test]
public void WillSubmitOrderWithSameIdAsTheOnePassedToController()
{
MockRepository mocks = new MockRepository();
IOrderService orderService = mocks.CreateMock<IOrderService>();
orderService.SubmitOrder(null);
LastCall.Constraints(Is.NotNull() && Property.Value("Id", 5));
mocks.ReplayAll();
OrderController controller = new OrderController(orderService);
int orderId = 5;
controller.SubmitOrder(orderId);
mocks.VerifyAll();
}
The only thing that I changed was the expectation, now I am not satisfied with merely a call to the service's SubmitOrder method, but I also assert that it is not null and that the Id property equals to 5. Running this test lead to the following error:
failed: IOrderService.SubmitOrder(null); Expected #0, Actual #1.
IOrderService.SubmitOrder(not equal to null and property 'Id' equal to 5); Expected #1, Actual #0.
You can bet that I am proud of Rhino Mocks for the above message :-) At any rate, let us change the SubmitOrder implementation so the test will pass:
[Transient]
public
{
public void SubmitOrder(int orderId)
{
Order o = GetOrderById(orderId);
orderService.SubmitOrder(o);
}
private static Order GetOrderById(int orderId)
{
Order o = new Order();
o.Id = orderId;
return o;
}
}
(Remember: I do not really care about the persistance of orders right now. This is simple and works). The tests works, but I see duplication in the tests that I can remove. The new test fixture looks like this:
[TestFixture]
public class OrderControllerFixture
{
IOrderService orderService;
MockRepository mocks;
private OrderController controller;
[SetUp]
public void TestInitialize()
{
mocks = new MockRepository();
orderService = mocks.CreateMock<IOrderService>();
controller = new OrderController(orderService);
}
[TearDown]
public void TestCleanup()
{
mocks.VerifyAll();
}
[Test]
public void WillSubmitOrderToService()
{
orderService.SubmitOrder(null);
LastCall.IgnoreArguments();
mocks.ReplayAll();
int orderId = 5;
controller.SubmitOrder(orderId);
}
[Test]
public void WillSubmitOrderWithSameIdAsTheOnePassedToController()
{
orderService.SubmitOrder(null);
LastCall.Constraints(Is.NotNull() && Property.Value("Id", 5));
mocks.ReplayAll();
int orderId = 5;
controller.SubmitOrder(orderId);
}
}
I removed the CanCreateOrderController, since I have moved it to the setup method anyway. Now, we need to have some way to notify the user, so we create a new test:
[Test]
public void WillReturnTrueIfOrderSubmittedSuccessfully()
{
orderService.SubmitOrder(null);
LastCall.IgnoreArguments();
mocks.ReplayAll();
int orderId = 5;
bool result = controller.SubmitOrder(orderId);
Assert.IsTrue(result, "Should return true is send was successful");
}
To make this test pass, I modified the SubmitOrder method to return a boolean and added a "return true" statement at the end of the method. Note that I do not care what I pass to the service's SubmitOrder() here, so I changed the expectation to a less restrictive one, in general, it is a best practice not to specify things that you aren't testing explicitly, otherwise, your tests may be extremely fragile.
Now that I got a passing test for a successful submittion, let us try to see what the failure mode that we have are. As far as I am concerned, there are two failure modes that I can identify at the moment, infrastructure failures (the service is down) or business failures (the submitted order is not valid, for instnace, not enough credit to pay it remained).
Now, let us think about the way WCF work, we have this seperation there as well, we have CommunicationException and FaultException, with the same overall meaning. I am going to make several assumptions now:
- Asserting that the configuration is correct is not the job of the OrderController, therefor, it assumes that the service it was configure to use is valid.
- A communication error is trasient in nature and should be retried if they failed, up to a pre-defined limit.
- A business errors cannot be recovered by a retry.
With those assumptions in mind, I can write the following test:
[Test]
public void WillReturnFalseIfOrderSubmittedRaisedFaultException()
{
orderService.SubmitOrder(null);
LastCall.IgnoreArguments()
.Throw(new FaultException("Just Because I felt like it"));
mocks.ReplayAll();
int orderId = 5;
bool result = controller.SubmitOrder(orderId);
Assert.IsFalse(result, "Should return true is send failed beause of a fault exeption");
}
This obviously fails, because the controller does not catch any exceptions. A simple implementation that fix it is:
public bool SubmitOrder(int orderId)
{
Order o = GetOrderById(orderId);
try
{
orderService.SubmitOrder(o);
}
catch
{
return false;
}
return true;
}
When I see code like this I usually wince, so we need a new test that will help me fix it, the easiest test that I can think about which would move us in the right direction is retries on communication failures:
[Test]
public void WillRetryThreeTimesIfCommunicationExceptionIsRaisedAndThenReturnFalse()
{
orderService.SubmitOrder(null);
LastCall.IgnoreArguments()
.Throw(new CommunicationException("Just Because I felt like it"))
.Repeat.Times(3);
mocks.ReplayAll();
int orderId = 5;
bool result = controller.SubmitOrder(orderId);
Assert.IsFalse(result, "Should return true is send failed beause of a communication exeption for three times");
}
Running this test produce this error:
failed: IOrderService.SubmitOrder(any); Expected #3, Actual #1.
Looks like I need to call SubmitOrder three times to make this test pass. I whipped up the following code:
public bool SubmitOrder(int orderId)
{
Order o = GetOrderById(orderId);
int retries = 3;
while (retries != 0)
{
try
{
orderService.SubmitOrder(o);
break;
}
catch (CommunicationException e)
{
retries -= 1;
if(retries==0)
return false;
}
catch
{
return false;
}
}
return true;
}
This is really ugly code, but let us try the tests first before we refactor. Hm, looks like this test pass, but the previous one (WillReturnFalseIfOrderSubmittedRaisedFaultException) now breaks. For some reason it is looks like the CommunicationException clause is catching the FaultException. Taking a look at the hierarchy, it looks like FaultException inherits from CommunicationException. This looks better:
public bool SubmitOrder(int orderId)
{
Order o = GetOrderById(orderId);
int retries = 3;
while (retries != 0)
{
retries -= 1;
try
{
orderService.SubmitOrder(o);
break;
}
catch(FaultException)
{
return false;
}
catch (CommunicationException)
{
if(retries==0)
return false;
}
}
return true;
}
It also passes all the test. I am still not really happy with the code above, I think that it is too complex, but at the moment I can't think of a good way to improve it in a way that would improve readabily. (For simplicity sake, I am explicitly ignoring user error message and logging).
Now, if it was a normal blog post about TDD, I would have declared at this point that TDD can bring peace to the middle east and go fishing toads from the pool, but I need to integrate that with WCF and my normal method of working, so this is harder.
The first problem is that I need to expose IOrderService and Order/OrderLine/Product to WCF:
[ServiceContract(Namespace = "http://www.ayende.com/WCF/TestDriven/")]
public interface IOrderService
{
[OperationContract]
void SubmitOrder(Order o);
}
And the Order class:
[DataContract(Namespace = "http://www.ayende.com/WCF/TestDriven/")]
public class Order
{
private int id;
private List<OrderLine> orderLines = new List<OrderLine>();
[DataMember]
public int Id
{
get { return id; }
set { id = value; }
}
[DataMember]
public List<OrderLine> OrderLines
{
get { return orderLines; }
set { orderLines = value; }
}
}
I assume that you can figure out how OrderLine and Product looks like ;-)
The second problem that I need to overcome is that I never create my controllers myself, I let Windsor do it for me:
OrderController controller = IoC.Resolve<OrderController>();
The problem here is that OrderController need to get a WCF Client Proxy, which Windsor has no idea how to give. This requires some thinking. WCF is configurable to the N-th degree, and I am probably no going to need most of this configuration power at all. This will soon make my life a lot simpler.
Now, Windsor doesn't know WCF, but it is extremely extensible, so all I need to do is to tell it: "Windsor, meet WCF" and it work with it. Let us build the WcfClientFacility. Before we start, what is a facility? A facility is a piece of code that extend Windsor, it has access to almost everything that Windsor can do, and it can modify the way Windsor (to the degree that 99% of the time you can just add functionality to Windsor without modifying Windsor itelf). Facilities has a lot of uses in Castle communities.
We start with the WcfClientFacility itself:
public class WcfClientFacility : AbstractFacility
{
private readonly WcfClientConfiguration[] registeredServicesConfigurations;
public WcfClientFacility(params WcfClientConfiguration[] registeredServicesConfigurations)
{
this.registeredServicesConfigurations = registeredServicesConfigurations;
}
protected override void Init()
{
Kernel.Resolver.AddSubResolver(new WcfClientResolver(registeredServicesConfigurations));
foreach (WcfClientConfiguration configuration in registeredServicesConfigurations)
{
ComponentModel model = new ComponentModel(configuration.Type.FullName, configuration.Type, configuration.Type);
model.CustomComponentActivator = typeof(WcfClientActivator);
model.LifestyleType = LifestyleType.Transient;
Kernel.AddCustomComponent(model);
}
}
}
The facility accept an array of WcfClientConfiguration, which contains the neccesary configration for a setting up the client. It first add a resolver (which we will touch in a minute) to the kernel, and then add all the interfaces to the container as components. I am doing both here because I would like to either declare a dependency in a constructor or a property (which is where the resolver comes to play) or I want to get a WCF Client directory from the container, like this:
IOrderService orderService = IoC.Resolve<IOrderService>();
A couple of things to note about the component registration, I am registering a custom component activator (which just forward to the resolver, as a matter of fact) and set the lifestyle as Transient, which means that a new instance will be created when it is requested. This is useful because in many cases, a client proxy may actualy be stateful (if the service on the other side uses sessions, for instance), or an server side error has turned the client unusable, in which case the last thing that you want is that the bad client will be returned on subsequent requests.
Now, let us look at the WcfActivator:
public class WcfClientActivator : AbstractComponentActivator
{
public WcfClientActivator(ComponentModel model, IKernel kernel,
ComponentInstanceDelegate onCreation, ComponentInstanceDelegate onDestruction)
: base(model, kernel, onCreation, onDestruction)
{
}
protected override object InternalCreate(CreationContext context)
{
Type type = context.Handler.ComponentModel.Service;
DependencyModel dependencyModel = new DependencyModel(DependencyType.Service, "wcf", type, false);
return Kernel.Resolver.Resolve(context, null, context.Handler.ComponentModel, dependencyModel);
}
/// <summary>
/// Can we tell WCF that we don't want an instance anymore?
/// Right now I don't know how, so we will live without it
/// </summary>
protected override void InternalDestroy(object instance)
{
}
}
This is called when a request to resolve a WCF component arrive, as you can see, it simply delegate the call to the resolver. The resolver is a bit complex, mostly because WCF doesn't support untyped model:
public class WcfClientResolver : ISubDependencyResolver
{
private readonly WcfClientConfiguration[] registeredServicesConfigurations;
private delegate object CreateChannel();
Dictionary<Type, CreateChannel> cachedDelegates = new Dictionary<Type, CreateChannel>();
public WcfClientResolver(WcfClientConfiguration[] registeredServicesConfigurations)
{
this.registeredServicesConfigurations = registeredServicesConfigurations;
}
public object Resolve(CreationContext context, ISubDependencyResolver parentResolver,
ComponentModel model, DependencyModel dependency)
{
foreach (WcfClientConfiguration proxy in registeredServicesConfigurations)
{
if (proxy.Type == dependency.TargetType)
{
CreateChannel createChannel = GetCreationDelegate(proxy);
return createChannel();
}
}
throw new InvalidOperationException(string.Format("Could not find a proxy type matching {0}", dependency.TargetType));
}
public bool CanResolve(CreationContext context, ISubDependencyResolver parentResolver,
ComponentModel model, DependencyModel dependency)
{
foreach (WcfClientConfiguration configuration in registeredServicesConfigurations)
{
if (dependency.TargetType == configuration.Type)
return true;
}
return false;
}
}
The resolver was created in the Init() method of the WcfClientFacility. It has two important method, CanResolve() (to which it will answer in the affirmative if the type that is a WCF [ServiceContract] that it had got) and Resolve(), which delegate the actual resolving to a delegate that it got from the GetCreationDelegate(). I am not showing the GetCreationDelegate above, here it is:
private CreateChannel GetCreationDelegate(WcfClientConfiguration configuration)
{
CreateChannel creation;
if (cachedDelegates.TryGetValue(configuration.Type, out creation))
return creation;
lock (cachedDelegates)
{
if (cachedDelegates.TryGetValue(configuration.Type, out creation))
return creation;
Type type = typeof(ChannelFactory<>).MakeGenericType(configuration.Type);
object target;
if (configuration.EndpointName != null)
{
//new ChannelFactory<T>(configuration.EndpointName).CreateChannel() - get the configuration from the app.config
target = Activator.CreateInstance(type, configuration.EndpointName);
}
else//we had the binding, contract explicty set for us.
{
// new ChannelFactory<T>(configuration.Binding, configuration.EndpointAddress).CreateChannel()
target = Activator.CreateInstance(type, configuration.Binding, configuration.EndpointAddress);
}
MethodInfo methodInfo = type.GetMethod("CreateChannel", new Type[] { });
creation = (CreateChannel)Delegate.CreateDelegate(typeof(CreateChannel), target, methodInfo);
cachedDelegates[configuration.Type] = creation;
return creation;
}
}
What does this do??
Well, the major issue is that WCF doesn't have untyped API, which means that I can't do something like "new ChannelFactory(typeof(IOrderService)).CreateChannel()". I have to call new ChannelFactory<IOrderService>().CreateChannel(). The code above deals with transforming the typed API to an untyped one.
One thing that is very important to note is that we have two ways to create the client, the first is by specifying an endpoint name, in which case it will go and look in the app.config for the configuration, the second is by specifying the EndpointAddress and Binding yourself, this will be useful in a moment.
The rest of this method just deal with creating a delegate to the CreateChannel method, and then storing it in a cache, for future reference.
Okay, so I made you sit through building a TDD service and adding a facility to Windsor, where am I driving at?
Well, consider what I can now do. We will start with the configuration:
import WCF.TDD
import System.ServiceModel
orderServiceClient = WcfClientConfiguration(
Type: IOrderService,
Binding: NetNamedPipeBinding(),
EndpointAddress: EndpointAddress("net.pipe://localhost/TDD.WCF.Order") )
IoC.Container.AddFacility( "wcf", WcfClientFacility( orderServiceClient ) )
Component("orderController", OrderController)
And move to the actual code:
IWindsorContainer container = new RhinoContainer("Windsor.boo");
OrderController orderController = container.Resolve<OrderController>();
We now have a fully functional orderController that can make use of a WCF client, and we didn't have to do anything special. As you saw before, we can mock this easily, so we have no dependency on WCF to run or test this code. To be rather exact, we do depend on WCF, after all, we need to catch WCF exception like CommuniationException and FaultException. The difference is that we have do dependency on a running service, which mean that we can build / test our application in isolation. For instance, the service that we outlined above doesn't even exist.
Now let us take it a bit further, shall we?
After testing my controller in isolation, I would like to test it during integration, but I have no control over the external service, and I certainly can't start sending requests to a service just for testing. (Mr. Foo get really annoyed when this happens). I still want to ensure that I am using WCF correctly, that I get the responses that I expect, etc. Considerring how complex WCF is, I think that this is certainly something that I ought to test.
Let us see how we can achieve that... here is what I want:
[TestFixture]
public class Integration_OrderControllerFixture
{
IOrderService orderServiceMock;
MockRepository serviceMockRepository;
Uri uri = new Uri("net.pipe://localhost/TDD.WCF.Order");
[TestFixtureSetUp]
public void TestFixtureInitialize()
{
serviceMockRepository = new MockRepository();
orderServiceMock = serviceMockRepository.CreateMock<IOrderService>();
}
[Test]
public void CanCallMockedWCFService()
{
orderServiceMock.SubmitOrder(null);
serviceMockRepository.ReplayAll();
IOrderService orderServiceClient = new ChannelFactory<IOrderService>(new NetNamedPipeBinding(), uri.AbsoluteUri)
.CreateChannel();
orderServiceClient.SubmitOrder(null);
}
[TearDown]
public void TestCleanup()
{
serviceMockRepository.VerifyAll();
}
}
Looks like I want to be able to make a call to the mocked instnace via WCF, how can we do it? We can utilize a WCF feature that let me pass it an initialized instance of a service. If we will pass it a mock object, what would happen?
I added a ServiceHost member variable, and initialized in the test fixture setup:
[TestFixtureSetUp]
public void TestFixtureInitialize()
{
container = new RhinoContainer("windsor.boo");
serviceMockRepository = new MockRepository();
orderServiceMock = serviceMockRepository.CreateMock<IOrderService>();
serviceHost = new ServiceHost(orderServiceMock, uri);
serviceHost.AddServiceEndpoint(typeof(IOrderService), new NetNamedPipeBinding(), uri);
serviceHost.Description.Behaviors.Find<ServiceBehaviorAttribute>()
.InstanceContextMode = InstanceContextMode.Single;
serviceHost.Open();
}
Here are a couple of interesting things to note:
- We create a RhinoContainer we the same configuration as we have above.
- We pass the service host the mock instance that we got from Rhino Mocks.
- We configure an end point for the interface.
- Finally, we specify that the instance mode for this service host is a singleton. (Required if you are passing an external instance.
Running the test now pass! We have successfully mocked a service on the other side of the wire!
Now let us implement the rest of the tests. Before I can add another test, however, I need to fix an issue with the fixture, I am using [TestFixtureSetUp] to build the service host and the mock object, but I am using [TearDown] (not [TestFixtureTearDown]) to verify the mock. The reason for this is that I have [Setup] and [TestFixtureTearDown] as well that I didn't show:
[SetUp]
public void TestInitialize()
{
serviceMockRepository.BackToRecord(orderServiceMock);
}
[TestFixtureTearDown]
public void TestFixtureCleanup()
{
serviceHost.Close();
}
The [SetUp] method will return the mocked orderServiceMock to recording state, and the [TestFixtureTearDown] will close the service host, freeing resources.
Now, for our successful test:
[Test]
public void WillSubmitOrderWithSameIdAsTheOnePassedToController()
{
orderServiceMock.SubmitOrder(null);
LastCall.Constraints(Is.NotNull() && Property.Value("Id", 5));
serviceMockRepository.ReplayAll();
int orderId = 5;
OrderController orderController = container.Resolve<OrderController>();
bool result = orderController.SubmitOrder(orderId);
Assert.IsTrue(result, "Should return true when successful");
}
What just happened?
I used everything that I talked about it before (WcfClientFacility, Mocks, and WCF) in order to get this test to work, let us break it to individual steps:
- We define an expectation that the service's SubmitOrder will be called with a non-null value whose Id equals 5. Note that this assertion is actually happening on the other side of the wire! In other words, what we are actually testing here is that the data that the controller sent was actually correctly recieved on the other end.
- We request a controller from the container, and get a fully intialized object, this included calling the WcfClientResolver and getting a client proxy from WCF.
- We make a call that eventually get back to the mock, where Rhino Mocks verify that the expectation is met.
Now, let us try something more complex, like throwing a FaultException:
[Test]
public void WillReturnFalseIfOrderSubmittedRaisedFaultException()
{
orderServiceMock.SubmitOrder(null);
LastCall.IgnoreArguments()
.Throw(new FaultException("Just Because I felt like it"));
serviceMockRepository.ReplayAll();
int orderId = 5;
OrderController orderController = container.Resolve<OrderController>();
bool result = orderController.SubmitOrder(orderId);
Assert.IsFalse(result, "Should return true is send failed beause of a fault exeption");
}
Yep, it just works :-)
However, trying to run this test:
[Test]
public void WillRetryThreeTimesIfCommunicationExceptionIsRaisedAndThenReturnFalse()
{
orderServiceMock.SubmitOrder(null);
LastCall.IgnoreArguments()
.Throw(new CommunicationException("Just Because I felt like it"))
.Repeat.Times(3);
serviceMockRepository.ReplayAll();
int orderId = 5;
OrderController orderController = container.Resolve<OrderController>();
bool result = orderController.SubmitOrder(orderId);
Assert.IsFalse(result, "Should return true is send failed beause of a fault exeption");
}
Result in a failure:
failed: IOrderService.SubmitOrder(any); Expected #3, Actual #1.
This looks like we only called the service SubmitOrder() once, but why? The reason here is that if WCF detect an exception that is not FaultException it will convert it to a FaultException (that is technically not correct, it is more complex than that, but this is enough).
Here is my new attempt:
[Test]
public void WillReturnFalseAndNotThrowIfCantContactService()
{
orderServiceMock.SubmitOrder(null);
LastCall.IgnoreArguments()
.Repeat.Never();
serviceMockRepository.ReplayAll();
serviceHost.Close();
int orderId = 5;
OrderController orderController = container.Resolve<OrderController>();
bool result = orderController.SubmitOrder(orderId);
Assert.IsFalse(result, "Should return true is send failed beause of a fault exeption");
}
What do I do here? I specify that the SubmitOrder should not be called, and then I close the service host, now if clients attempt to access it, they will get CommunicationException, which is what I wanted to test.
Note that I changed the name of the test, because it is no longer testing that we are retrying three times. Usually, in those cases I would rely on the logging information to verify that (and verify that the logs are going out :-) ), in this case, it is a demo and I already have the unit tests that verify that this is indeed the correct behavior.
Well, that was long enough, by my count, this is over 20 printed pages, which probably means that you already gave up on this post long ago. At any rate, I planned to post about how to integrate WCF services (and not just clients) into Windsor, but I believe that this can certainly wait for another day.
You can find the code here, have fun.
Comments
AMAZING WORK!
I call this the prevailing WCF real-world blog post on the internet right now.
Do you sleep?
Pretty cool, and pretty nifty, but I'm not sure if it's really worth it to add all that complexity around just to add the test. Maybe I'm missing something, but just how much value do you get over simply mocking the service interface on the client side locally and leaving the WCF code out of the picture completely?
Unbelievable,
someone actually read it to the end :-)
I actually do sleep, monday 10AM - 11AM, ever other week :-)
This is actually more than just about mocking WCF.
It is about integrating a WCF service directly into the application, in a way that is mostly transparent to my application.
This means that I can get a WCF service from the container without needing to worry about anything else that it might need.
I probably should have broken the post into several posts, but I was on a role and couldn't face the attempt to split it into standalone logical units.
A story from the real world:
I once had a web service client that would start consume 100% CPU for a long time, if and only if it pulled over 500 messages in a span of three minutes. Trying to reproduce that without using the real service failed, eventually I managed to track the problem down to the way accepting such a large load of messages would strain the database, which would kick off the system's auto recovery mode, which...
This is something that you are only ever going to find out when you are doing integration tests, and when that is the case, I really want to be able to test in this scenario, which include testing through the WCF pipeline itself.
I get wanting to run the service inside WCF for integration tests. I've done that very successfully before and I agree it's a great idea. What maybe confused me was the reference to mocking.
To put it this way: Mocking the service interface? great! extending windsor to allow it to instantiate WCF service proxies? Great! Going through Windsor and WCF to get to a mocked service instance? hummm... not sure exactly the benefit of it here.
Think about the real application.
My components are getting their WCF services from Windsor.
When I need to do integration tests, they are still getting them from Windsor. And the instances that they get are not mocked.
What is mocked in this case is the implementation of the service that is running on the other side of WCF.
I am not going through Windsor to get the mocked instance, I am working directly with WCF to mock the service instance.
I understood that, Oren, I just don't see what exactly is it that you're gaining by that after going through all that trouble. Maybe I just prefer to keep things simpler unless there is something definite and clear that absolutely wins me something, or, most likely, it's just me being thickheaded today.
Mocking Windows Communication Foundation (WCF)
nice place to get knoledge
Comment preview