[转载]新浪微博客户端围脖七开发笔记(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