使用Moq模拟Silverlight WCF代理中的异步调用
我有一个具有WCF服务的典型Silverlight应用程序,我正在使用slsvcutil.exe生成标准客户端代理以与Web服务进行通信。 我正在尝试编写单元测试,并试图使用Silverlight单元测试框架和Moq来模拟代理并删除服务依赖项以进行测试。
我对Moq非常陌生,并且在进行服务调用以模拟异步调用时自动提升模拟代理上的各种Completed事件时遇到许多麻烦。
为了使代理“可嘲笑”,我为生成的代理调用及其完成的事件创建了自己的简单界面:
public interface IServiceProxy
{
void TestAsync(TestRequest request);
void TestAsync(TestRequest request, object userState);
event EventHandler<TestCompletedEventArgs> TestCompleted;
}
我还将生成的代理对象分类为实现该接口:
public class MyServiceProxy : GeneratedServiceClient, IServiceProxy, ICommunicationObject
{
// ... overloaded proxy constructors
}
在查看Moq文档后,我正在尝试设置模拟以期待TestAsync()调用,并立即使用EventArgs中的结果引发TestCompleted事件:
[TestMethod]
public void Test_Returns_Expected()
{
var mockProxy = new Mock<IServiceProxy>();
var result = new TestResponse() { Value = true };
this.mockProxy.Setup(
p => p.TestAsync(It.IsAny<TestRequest>()))
.Raises(p => p.TestCompleted += null, new TestCompletedEventArgs(new object[] { result }, null, false, null));
// rest of the test to actually use the mock and assert things
}
一切都很好,但是当我尝试使用模拟和设置断点来运行任何类型的测试时,TestCompleted事件在我调用TestAsync()时从不会引发。
有没有什么明显的我失踪或任何更好的想法在Silverlight中嘲笑这些类型的异步服务代理?
谢谢!
编辑:
为了更清楚我实际上试图测试的是我制作的一个帮助器类,它接受IServiceProxy
一个实例,并通过接受Action<TResponse, Exception>
回调参数而不是处理回调事件Action<TResponse, Exception>
为我的ViewModel提供更清洁的服务接口在我的ViewModel中。 我明白我怎么可以嘲笑这个以及直接测试我的ViewModel,但我想先测试这个助手类会很好。
这是我正在谈论的一个例子:
public class HelperClient : IServiceHelper
{
private IServiceProxy proxy;
public HelperClient(IServiceProxy proxy)
{
this.proxy = proxy;
// register to handle all async callback events
this.proxy.TestCompleted += new EventHandler<TestCompletedEventArgs>(TestCompleted);
}
public void Test(TestRequest request, Action<TestResponse, Exception> response)
{
this.proxy.TestAsync(request, response);
}
private void TestCompleted(object sender, TestCompletedEventArgs e)
{
var response = e.UserState as Action<TestResponse, Exception>;
if (response != null)
{
var ex = GetServiceException(e);
if (ex == null)
{
response(e.Result, null);
}
else
{
response(null, ex);
}
}
}
}
所以在我的测试中,我真正在做的是嘲笑ISerivceProxy并将其传入,并试图测试服务调用以确保它正确调用Action的包装:
[TestMethod]
[Asynchronous]
public void Test_Returns_Expected()
{
var mockProxy = new Mock<IServiceProxy>();
var helper = new HelperClient(mockProxy.Object);
bool expectedResult = true;
var result = new TestResponse() { Value = expectedResult };
this.mockProxy.Setup(
p => p.TestAsync(It.IsAny<TestRequest>()))
.Raises(p => p.TestCompleted += null, new TestCompletedEventArgs(new object[] { result }, null, false, null));
helper.Test(new TestRequest(), (response, ex) =>
{
Assert.AreEqual(expectedResult, response.Value);
EnqueueTestComplete();
});
}
问题在于,模拟代理对象永远不会引发TestCompleted事件,所以我的响应操作永远不会被调用来完成测试(即使测试似乎成功完成,Assert从未实际运行)。 对不起,这么长的帖子,只是试图向你展示尽可能多的代码。
编辑2
添加[Asynchronous]
并调用EnqueueTestComplete()
,我意识到可能需要让测试等待事件的发生。 这并没有真正的帮助,事件仍然没有提出,所以测试只是挂起,并没有完成。
编辑3
Aliostad的回答是正确的,我的设置期望的签名与实际的Test()签名不匹配,允许我将响应Action作为第二个参数传递。 愚蠢的错误,但这正是阻止Moq提高Completed事件的原因。 我还忘记了将Action作为TestCompletedEventArgs中的userState对象传递,以便在引发Completed事件时实际调用它。 此外,在这种情况下, [Asynchronous]
和EnqueueTestCompleted
似乎不是必需的。
这里是更新测试代码的任何感兴趣的人:
[TestMethod]
public void Test_Returns_Expected()
{
var mockProxy = new Mock<IServiceProxy>();
var helper = new HelperClient(mockProxy.Object);
bool expectedResult = true;
var result = new TestResponse() { Value = expectedResult };
Action<TestResponse, Exception> responseAction = (response, ex) =>
{
Assert.AreEqual(expectedResult, response.Value);
};
this.mockProxy.Setup(
p => p.TestAsync(It.IsAny<TestRequest>(), It.IsAny<Action<TestResponse, Exception>>()))
.Raises(p => p.TestCompleted += null, new TestCompletedEventArgs(new object[] { result }, null, false, responseAction));
helper.Test(new TestRequest(), responseAction);
}
嘲讽事件非常痛苦,单元测试变得脆弱。 但正如你所说,它无法绕过它。 但是,通常情况下,您会进行要尝试的调用并阻止当前线程(使用Sleep或其他方法),直到引发事件(或超时)。
实际上你不清楚你正在测试什么。 我可以看到一个模拟和回应,实际的真实对象在哪里?
我会相应地更新我的答案。
UPDATE
我可以在这里看到一个问题:
helper.Test(new TestRequest(), (response, ex) =>
{
Assert.AreEqual(expectedResult, response.Value);
EnqueueTestComplete();
});
在最后的声明中,你正在把EnqueueTestComplete(); 你断言,但这个动作永远不会被使用,因为它传递给moq对象。
你也正在设置的期望TestAsync(It.IsAny<TestRequest>()))
一个参数),而你是在这两个参数调用它HelperClient
( this.proxy.TestAsync(request, response);
),这是为什么它不会因为没有达到预期而被解雇。
刚刚搜索模拟异步WCF客户端,发现这个问题。
为防止这种情况可以起订量.Verify()
是p.TestAsync()被调用。
//will throw MockException if p.TestAsync() has never been called.
this.mockProxy.Verify(p => p.TestAsync(It.IsAny<TestRequest>()), Times.Once());
链接地址: http://www.djcxy.com/p/48251.html
上一篇: Mocking Asynchronous Calls in Silverlight WCF Proxy using Moq