I'm trying to write some code to help unit test WCF services. These services are accessed through a facade class that creates the proxy instance, then calls the proxy method and returns the result; for each proxy method. I'd like to be able to replace the current creation code with something that either creates the real service or a fake one.
I couldn't get that to work. I boiled it down to the following:
using System.ServiceModel;
namespace ExpressionTrees
{
public interface IMyContract
{
void Method();
}
public class MyClient : ClientBase<IMyContract>, IMyContract
{
public MyClient()
{
}
public MyClient(string endpointConfigurationName)
: base(endpointConfigurationName)
{
}
public void Method()
{
Channel.Method();
}
}
public class Test
{
public TClient MakeClient<TClient>()
where TClient : ClientBase<IMyContract>, IMyContract, new()
{
return new MyClient("config");
// Error:
// Cannot convert expression of type 'ExpressionTrees.ServiceClient' to return type 'TClient'
}
}
}
Why is it that, even though the MyClient
class derives from ClientBase<IMyContract>
and implements IMyContract
, that I can't return a MyClient
instance from a method meant to return a TClient
? TClient
specifies a type constraint I had thought meant the same thing.
My goal was to call code like this:
public void CallClient<TClient>()
where TClient : ClientBase<IMyContract>, IMyContract
{
TClient client = null;
bool success = false;
try
{
client = MakeClient<TClient>();
client.Method();
client.Close();
success = true;
}
finally
{
if (!success && client != null)
{
client.Abort();
}
}
}
But instead of always calling MakeClient<TClient>
, I wanted to be able to have a unit test inject a mock object. Since the code above depends both on ClientBase<IMyContract>
, IMyContract
, it seems I was trying to "synthesize" a generic class that would satisfy that constraint.
In retrospect, this wouldn't make sense. As an example, the ClientBase<IMyContract>
would expect to be instantiated in such a way that a Channel
object would be constructed that it could then delegate the Close
method to.
I've wound up punting on having the exact same code run both for the real and for the fake services. I'm now injecting an IMyService
, and either calling IMyService.Method
or client.Method
depending on whether my injected property is null.
Thanks for the suggestions, which showed me where my thinking was wrong.