I am using FakeItEasy, which doesn't allow for mocking of OperationContext because it's a sealed class. Add dependency injection into the mix, and you've got yourself a straight-up nightmare. I've spent a week trying to figure this stuff out, and here's what I finally came up with . . . you're going to create a couple of ServiceHost
's, and then they're going to talk to each other and run all your code in between.
#1 Create a class that inherits from ClientBase<IMyWcfService>
:
public class MyWcfServiceClient : ClientBase<IMyWcfService>, IMyWcfService
{
public MyWcfServiceClient(string address)
: base(new WebHttpBinding(), new EndpointAddress(address))
{
this.Endpoint.EndpointBehaviors.Add(new WebHttpBehavior());
}
public void CallMyStuff()
{
using (new OperationContextScope(this.InnerChannel))
{
base.Channel.CallMyStuff();
}
}
}
#2 Create a "calling service" that will call this method. First, create an interface:
[ServiceContract]
public interface IMyWcfCallingService
{
[OperationContract]
void CallCallMyStuff();
}
Then create the "calling service" that implements this interface:
public class MyWcfCallingService : IMyWcfCallingService
{
static MyWcfServiceClient _client = new MyWcfServiceClient("http://localhost:8008");
// ^^^ This "http://localhost:8008" is the address where
// your actual service is going to "live" in your unit test
public void CallCallMyStuff()
{
_client.CallMyStuff();
}
}
#3 Instantiate your actual service in the unit test:
var myService = new MyWcfService(_someMockedDependency, _someOtherMockedDependency);
#4 Create the two ServiceHost
's that are going to talk to one another, and call your service:
var restHost = new WebServiceHost(myService, new Uri("http://localhost:8008"));
// ^^^ Make sure the URL here matches the URL you used in Step #2
var behavior = restHost.Description.Behaviors.Find<ServiceBehaviorAttribute>();
behavior.InstanceContextMode = InstanceContextMode.Single;
// ^^^ If you're using dependency injection with mocked dependencies
// to create your myService object, this is muy importante
restHost.Open();
var clientHost = new ServiceHost(typeof(MyWcfCallingService), new Uri("http://localhost:80"));
// ^^^ use some other address here, different from the one you used for the service itself
clientHost.AddServiceEndpoint(typeof(IMyWcfCallingService), new BasicHttpBinding(), string.Empty);
clientHost.Open();
var factory = new ChannelFactory<IMyWcfCallingService>(new BasicHttpBinding(), new EndpointAddress("http://localhost:80"));
// ^^^ this should match the url for the clienthost
var proxy = factory.CreateChannel();
proxy.CallCallMyStuff();
Damn WCF all to hell! I never appreciated WebApi so much as when I started having to dig through legacy WCF code.