I recently developed a Silverlight application which uses Mark J Millers ClientChannelWrapper<T>
to communicate with the WCF service layer (effectively killing the service reference and wrapping IClientChannel
and ClientChannelFactory
).
Here is the interface:
public interface IClientChannelWrapper<T> where T : class
{
IAsyncResult BeginInvoke(Func<T, IAsyncResult> function);
void Dispose();
void EndInvoke(Action<T> action);
TResult EndInvoke<TResult>(Func<T, TResult> function);
}
The Wrapper basically takes a generic async service interface (which might have been generated by slsvcutil or hand crafted after the WCF ServiceContract
) and wraps the calls to ensure that in case of a channel fault, a new channel gets created.
Typical usage looks like this:
public WelcomeViewModel(IClientChannelWrapper<IMyWCFAsyncService> service)
{
this.service = service;
this.synchronizationContext = SynchronizationContext.Current ?? new SynchronizationContext();
this.isBusy = true;
this.service.BeginInvoke(m => m.BeginGetCurrentUser(new AsyncCallback(EndGetCurrentUser), null));
}
private void EndGetCurrentUser(IAsyncResult result)
{
string strResult = "";
service.EndInvoke(m => strResult = m.EndGetCurrentUser(result));
this.synchronizationContext.Send(
s =>
{
this.CurrentUserName = strResult;
this.isBusy = false;
}, null);
}
It all works fine but now I'd like to unit test the view models which use the ClientChannelWrapper
.
I've set up a simple unit test using Moq:
[TestMethod]
public void WhenCreated_ThenRequestUserName()
{
var serviceMock = new Mock<IClientChannelWrapper<IMyWCFAsyncService>>();
var requested = false;
//the following throws an exception
serviceMock.Setup(svc => svc.BeginInvoke(p => p.BeginGetCurrentUser(It.IsAny<AsyncCallback>(), null))).Callback(() => requested = true);
var viewModel = new ViewModels.WelcomeViewModel(serviceMock.Object);
Assert.IsTrue(requested);
}
I get a NotSupportedException:
Unsupported expression: p => p.BeginGetCurrentUser(IsAny(), null).
I'm pretty new to Moq but I guess there is some problem with the ClientChannelWrapper
using generic Service interfaces. Trying to wrap my head around this for quite some time now, maybe someone has an idea. Thank you.
Sorry to answer my own question, but I finally got it. Here are the details:
As so often, the solution was right in front of me since it was in Mark J Millers concrete implementation of the IClientChannelWrapper. In there he provides two constructors, one taking in a string of the WCF endpoint name (which I use in production code) and a second one:
public ClientChannelWrapper(T service)
{
m_Service = service;
}
Without further ado, here's my new test code:
[TestMethod]
public void WhenCreated_ThenRequestUserName()
{
var IMySvcMock = new Mock<IMyWCFAsyncService>();
var serviceMock = new ClientChannelWrapper<IMyWCFAsyncService>(IMySvcMock.Object);
var requested = false;
IMySvcMock.Setup(svc => svc.BeginGetCurrentUser(It.IsAny<AsyncCallback>(), null)).Callback(() => requested = true);
var viewModel = new ViewModels.WelcomeViewModel(serviceMock);
Assert.IsTrue(requested);
}
So I basically ignore the interface of the ChannelWrapper and create a new instance of it with the Mock Object of my WCF service interface. Then I setup the call to the service which will be used in the constructor of my view model as shown above. Everything works like a charm now. I hope this is useful for someone since I think the idea the of the ClientChannelWrapper is great for Silverlight <-> WCF communication. Please feel free to comment on this solution!