C# 对 System.Reactive 可观察对象进行单元测试
一、问题
假设程序的某一部分正在使用 IObservable<T>
,而你需要想办法对它进行单元测试。
二、解决方案
System.Reactive 有一些可以生成序列的运算符(比如 Return
),还有可以将响应式序列转换为常规集合或项的其他运算符(比如 SingleAsync
)。可以使用 Return
之类的运算符来创建可观察依赖项的存根,使用 SingleAsync
之类的运算符来测试输出。
观察下面的代码,其中将 HTTP 服务作为依赖项并对 HTTP 请求运用了超时:
public interface IHttpService
{
IObservable<string> GetString(string url);
}
public class MyTimeoutClass
{
private readonly IHttpService _httpService;
public MyTimeoutClass(IHttpService httpService)
{
_httpService = httpService;
}
public IObservable<string> GetStringWithTimeout(string url)
{
return _httpService.GetString(url)
.Timeout(TimeSpan.FromSeconds(1));
}
}
被测试的系统是 MyTimeoutClass
,它消耗了可观察对象的依赖项,并生成了一个可观察对象作为输出。
Return
运算符创建的冷序列只包含一个元素,通过 Return
可以创建简单的存根。SingleAsync
运算符返回的 Task<T>
会在下一个事件到来时完成。SingleAsync
可以用在类似下面的简单单元测试中:
class SuccessHttpServiceStub : IHttpService
{
public IObservable<string> GetString(string url)
{
return Observable.Return("stub");
}
}
[TestMethod]
public async Task MyTimeoutClass_SuccessfulGet_ReturnsResult()
{
var stub = new SuccessHttpServiceStub();
var my = new MyTimeoutClass(stub);
var result = await my.GetStringWithTimeout("http://exampleurl")
.SingleAsync();
Assert.AreEqual("stub", result);
}
在存根代码中,另一个重要的运算符是 Throw
,它返回一个以错误结束的可观察对象。该运算符还可以对错误用例进行单元测试,下例使用了 7.2 节中的 ThrowsAsync
辅助方法。
private class FailureHttpServiceStub : IHttpService
{
public IObservable<string> GetString(string url)
{
return Observable.Throw<string>(new HttpRequestException());
}
}
[TestMethod]
public async Task MyTimeoutClass_FailedGet_PropagatesFailure()
{
var stub = new FailureHttpServiceStub();
var my = new MyTimeoutClass(stub);
await ThrowsAsync<HttpRequestException>(async () =>
{
await my.GetStringWithTimeout("http://exampleurl")
.SingleAsync();
});
}
三、讨论
Return
和 Throw
是创建可观察存根的极好选择,SingleAsync
则是通过 async
单元测试来测试可观察对象的便捷之道。可以将它们组合起来,这非常适合用于简单的可观察对象,但一旦涉及时间,它们就不那么好用了。假如测试 MyTimeoutClass
的超时负荷,单元测试必须等待对应的时长。然而这个办法很糟糕:由于引入了竞争条件,因此单元测试变得不可靠,而且当添加更多单元测试后,其扩展性也不佳。
(完)
相关阅读:
C# 对 async 方法进行单元测试
C# 对预期失败的 async 方法进行单元测试
C# 对 async void 方法进行单元测试
C# 对数据流网格进行单元测试
C# 对 System.Reactive 可观察对象进行单元测试
C# 通过伪造调度对 System.Reactive 可观察对象进行单元测试
评论
发表评论