Flurl.Http提供了一组测试特性,这些特性使得测试变得非常简单。Flurl.Http的核心是HttpTest,它创建HttpTest实例将Flurl带入测试模式,测试主题中的所有HTTP活动都会自动伪造并记录,比如:
using Flurl.Http.Testing;
[Test]
public void Test_Some_Http_Calling_Method() {
using (var httpTest = new HttpTest()) {
// Flurl现在进入了测试模式
sut.CallThingThatUsesFlurlHttp(); // 模拟HTTP请求!
}
}
大多数单元测试框架都有一些setup
或者teardown
方法的概念,这些方法在每个测试之前/之后执行。对于有很多针对http调用代码的测试的类,您可能更喜欢下面这种方法:
private HttpTest _httpTest;
[SetUp]
public void CreateHttpTest() {
_httpTest = new HttpTest();
}
[TearDown]
public void DisposeHttpTest() {
_httpTest.Dispose();
}
[Test]
public void Test_Some_Http_Calling_Method() {
// Flurl处于测试模式
}
注意:由于SUT中用于信号调用伪造的机制存在已知问题,从异步设置方法实例化HttpTest将无法正常工作。
默认情况下,模拟的HTTP调用返回一个200(OK)状态和一个空的主体。当然,你可能希望根据其他响应测试你的代码。
httpTest.RespondWith("响应数据");
sut.DoThing();
使用json对象的响应:
httpTest.RespondWithJson(new { x = 1, y = 2 });
测试失败的条件:
httpTest.RespondWith("server error", 500);
httpTest.RespondWithJson(new { message = "unauthorized" }, 401);
httpTest.SimulateTimeout();
以RespondWith*
开头的方法都是链式的,如下:
httpTest
.RespondWith("some response body")
.RespondWithJson(someObject)
.RespondWith("error!", 500);
sut.DoThingThatMakesSeveralHttpCalls();
在后台,每个RespondWith*
都会向线程安全队列添加一个假响应。
从Flurl 3.0开始,你还可以设置仅适用于匹配特定条件的请求的响应。以下代码演示了所有可能的条件:
httpTest
.ForCallsTo("*.api.com*", "*.test-api.com*") // 多个请求地址,支持通配符
.WithVerb("put", "PATCH") // 或者 HttpMethod.Put, HttpMethod.Patch
.WithQueryParam("x", "a*") // 参数值,支持通配符
.WithQueryParams(new { y = 2, z = 3 })
.WithAnyQueryParam("a", "b", "c")
.WithoutQueryParam("d")
.WithHeader("h1", "f*o") // 参数值,支持通配符
.WithoutHeader("h2")
.WithRequestBody("*something*") // 支持通配符
.WithRequestJson(new { a = "*", b = "hi" }) // 字符串支持通配符
.With(call => true) // 检查FlurlCall上的所有条件
.Without(call => false) // 检查FlurlCall上的所有条件
.RespondWith("所有条件都满足!", 200);
如果在某些情况下需要发起真正的请求?调用AllowRealHttp()
方法即可,如下:
httpTest
.ForCallsTo("https://api.thirdparty.com/*")
.AllowRealHttp();
一旦HttpTest
被创建且任何特定的响应都进入了队列,则只需要简单地调用一个测试主题即可。当SUT使用Flurl进行HTTP调用时,真正的调用会被有效地阻塞,而下一个模拟的响应会被离队并返回。
然而,当队列中只有一个响应(如果提供了匹配任何筛选条件)时,该响应就会变得“粘性”,也就是说,它没有退出队列,因此会在所有后续调用中返回。
不需要模拟或存根任何Flurl对象来实现这个功能。HttpTest使用逻辑异步调用上下文在SUT中传递信号,并通知Flurl模拟调用。
由于HTTP调用是伪造的,它们将自动记录到一个调用日志中,从而允许您断言某些调用是已经发出。断言与测试框架无关;当找不到指定的匹配项时,它们会在任何时候抛出异常,这实际上表明了所有测试框架中的测试失败。
HttpTest针对调用日志提供了两种断言方法:
sut.DoThing();
httpTest.ShouldHaveCalled("http://some-api.com/*");
httpTest.ShouldNotHaveCalled("http://other-api.com/*");
httpTest.ShouldHaveMadeACall();
httpTest.ShouldNotHaveMadeACalled();
当然,您可以对特定的调用进行进一步的断言:
httpTest.ShouldHaveCalled("http://some-api.com/*")
.WithQueryParam("x", "1*")
.WithVerb(HttpMethod.Post)
.WithContentType("application/json")
.WithoutHeader("my-header-*")
.WithRequestBody("{\"a\":*,\"b\":*}")
.Times(3);
Times(n)
允许你断言调用被执行了特定的次数;否则,当进行一个或多个匹配调用时,将传递断言。在所有可以传递名称和值的情况下,null值(默认值)意味着忽略并仅断言名称。和测试设置标准一样,*
通配符几乎到处都被支持。
当With*
方法不能提供您需要的所有内容时,您可以向下一层并直接断言调用日志:
Assert.That(httpTest.CallLog.Any(call => /* 断言当前调用 */));
CallLog
是一个IList<FlurlCall>
集合。一个FlurlCall
对象包含了很多的有用信息,详情参考这里。
发表评论
登录用户才能发表评论, 请 登 录 或者 注册