新浪微博客户端围脖七开发笔记(3) 如何添加异步测试并模拟异步方法

这几天在研究如何进行异步测试, silverlight unit test framework提供了相应支持,异步在这个framework中的含义就是把一些额外的任务排队并稍后执行。 比如有一个request()方法是异步的,一般情况下呼叫这个方法之后无法直接测试返回的结果;但在测试方法TestRequest()中,呼叫request()后可以加入一些额外任务到一个队列中,在退出TestRequest()后执行这些任务。说起来有些拗口看下图会明白些:

左边是正常的测试方法,右边是异步测试

一般运用下面四个方法添加任务到队列中:

EnqueueTestComplete() – 添加一个TestComplete()到队列中,这个方法告知framework测试结束了。
EnqueueCallback() – 添加一个任务到队列
EnqueueConditional() – 添加一个条件判断到队列,如果为true才继续执行
EnqueueDelay() – 添加一些等待时间到队列

下图演示了如何实际使用:

下面回到项目中,看看具体如何做:

1.添加一个MockSinaService:

代码
public class MockSinaService : ISinaService
{
public List<Status> StatusList{get;set;}

public void GetStatuses(System.Action<IEnumerable<Status>> onGetStatusesCompleted = null, System.Action<System.Exception> onError = null, System.Action onFinally = null)
{
DispatcherTimer timer
= new DispatcherTimer();
timer.Interval
= TimeSpan.FromSeconds(2);
timer.Tick
+= delegate(object sender, EventArgs e)
{
Status testStatus
=
new Status
{
Text
= "this is test status",
CreatedAt
= DateTime.Now
};

onGetStatusesCompleted(
new List<Status>() { testStatus });
timer.Stop();
};
timer.Start();
}
}
这个类是为了模拟SinaService,减少MainPageViewModel对它的依赖。因为实际中SinaService会调用webclient异步取得数据,但MainPageViewModel其实并不关心数据是怎么来的,添加这个mock会方便单元测试。注意在实现中使用定时器来模拟webclient的异步行为。

2. 添加一个公共属性SinaService到MainPageViewModel中,这样测试项目可以把它指向mock类

代码
public ISinaService SinaService
{
get
{
if (_sinaService == null)
{
_sinaService
= new SinaService();
}
return _sinaService;
}
set
{
if (value != _sinaService)
{
_sinaService
= value;
}
}
}
3. 添加RefreshHomeList()方法到MainPageViewModel中

代码
public void RefreshHomeList()
{
Trace.DetailMsg(
"RefreshHomeList");
IsHomeRefreshing
= true;
SinaService.GetStatuses(
delegate(IEnumerable<Status> statuses)
{
IsHomeRefreshing
= false;

foreach (Status status in statuses)
{
HomeList.Add(status);
}
},
delegate(Exception exception)
{
IsHomeRefreshing
= false;
});
}
4. 修改TestInitialize如下,植入mock类

代码
[TestInitialize]
public void Initialize()
{
_mainViewModel
= new MainPageViewModel();
_mockSinaService
= new MockSinaService();
_mainViewModel.SinaService
= _mockSinaService;
}
5. 添加测试方法:

代码
[TestMethod]
[Asynchronous]
public void Refresh_HomeList_Success()
{
bool isHomeListRefreshed = false;
_mainViewModel.HomeList.CollectionChanged
+=
(s, e)
=>
{
isHomeListRefreshed
= true;
Assert.AreEqual(NotifyCollectionChangedAction.Add, e.Action);
Assert.AreEqual(
1, e.NewItems.Count, "only should be +1 item");
};
_mainViewModel.RefreshHomeList();
EnqueueConditional(()
=> isHomeListRefreshed);
EnqueueCallback(()
=> Assert.AreEqual(_mainViewModel.HomeList.Count, 1, "Expected non-empty products list."));
EnqueueTestComplete();

 

注意这里的Asynchronous关键字,而且要让MainPageViewModelTests这个类继承SilverlightTest。

最后测试结果如下:

 

References:

1. silverlight2-unit-testing by jeff wilcox

2. Asynchronous test support – Silverlight unit test framework and the UI thread by jeff wilcox

3. Silverlight Unit Testing, RhinoMocks, Unity and Resharper by Justin Angel

 

posted @ 2010-11-25 20:35  上尉  阅读(2062)  评论(3编辑  收藏  举报
View Hao Wang's profile on LinkedIn