使用Dunitx在Delphi中的HTTP请求上进行固执和设定期望的库。
* Webmocks是在Delphi 10.3(Rio)和10.4(悉尼)中开发的,直到版本3.0与XE8兼容。由于WebMocks使用了XE8引入的System.Net库,因此与早期版本不兼容。如果您需要在10.3之前安装Delphi版本,则应安装2.0.0版。
Webmocks 3.2.0可通过Embarcadero的Delphi Getit的软件包管理器获得。如果您有包括Getit在内的Delphi的最新版本,那么这应该是首选的安装方法。
现在应在Delphinus软件包管理器中列出Webmock。
通过Delphinus安装后,请确保重新启动Delphi,否则在您的测试项目中可能找不到单元。
Source目录添加到“库路径”和“浏览路径”中。 如果您想对Delphi的网络货物进行温和的介绍,则在DEV上发表了一系列文章,从DELPHI的HTTP客户端使用Dunitx和Webmocks进行了测试。
Delphi-Webmocks-Demos包含一组示范,以伴随文章。
版本2删除了Delphi.来自所有单元的名称空间。任何项目升级到版本2或更高版本都需要删除Delphi.来自任何包含的网络货物单元的前缀。
在您的测试单元文件中,需要几个简单的步骤。
WebMock添加到接口uses 。TestFixture类中,使用Setup和TearDown来创建/破坏TWebMock的实例。 unit MyTestObjectTests;
interface
uses
DUnitX.TestFramework,
MyObjectUnit,
WebMock;
type
TMyObjectTests = class (TObject)
private
WebMock: TWebMock;
Subject: TMyObject;
public
[Setup]
procedure Setup ;
[TearDown]
procedure TearDown ;
[Test]
procedure TestGet ;
end ;
implementation
procedure TMyObjectTests.Setup ;
begin
WebMock := TWebMock.Create;
end ;
procedure TMyObjectTests.TearDown ;
begin
WebMock.Free;
end ;
procedure TMyObjectTests.TestGet ;
begin
// Arrange
// Stub the request
WebMock.StubRequest( ' GET ' , ' /endpoint ' );
// Create your subject and point it at the endpoint
Subject := TMyObject.Create;
Subject.EndpointURL := WebMock.URLFor( ' endpoint ' );
// Act
Subject.Get;
// Assert: check your subject behaved correctly
Assert.IsTrue(Subject.ReceivedResponse);
end ;
initialization
TDUnitX.RegisterTestFixture(TMyObjectTests);
end .默认情况下, TWebMock将绑定到8080的动态分配的端口。可以通过在Creation中指定端口来覆盖此行为。
WebMock := TWebMock.Create( 8088 );在测试中使用WebMock.URLFor功能是简化构建有效URL的方法。 Port属性包含当前绑定端口,而BaseURL属性包含服务器根的有效URL。
所有请求存根的请求匹配和起点的最简单形式是通过HTTP方法和文档路径。例如,用http动词GET到服务器root /是通过:
WebMock.StubRequest( ' GET ' , ' / ' );单个通用卡字符*的使用可用于匹配任何请求。例如,要匹配所有POST请求,无论文档路径如何,都可以使用:
WebMock.StubRequest( ' POST ' , ' * ' );同样,要匹配给定路径的任何HTTP方法,您可以使用:
WebMock.StubRequest( ' * ' , ' /path ' );对于HTTP方法和文档路径,可以完全有可能拥有*和* 。
HTTP请求标头可以匹配:
WebMock.StubRequest( ' * ' , ' * ' ).WithHeader( ' Name ' , ' Value ' );可以通过两种方式实现多个标头。首先是简单地WithHeader调用,例如:
WebMock.StubRequest( ' * ' , ' * ' )
.WithHeader( ' Header-1 ' , ' Value-1 ' )
.WithHeader( ' Header-2 ' , ' Value-2 ' );另外, WithHeaders接受键值对的TStringList ,例如:
var
Headers: TStringList;
begin
Headers := TStringList.Create;
Headers.Values[ ' Header-1 ' ] := ' Value-1 ' ;
Headers.Values[ ' Header-2 ' ] := ' Value-2 ' ;
WebMock.StubRequest( ' * ' , ' * ' ).WithHeaders(Headers);
end ;HTTP请求可以与内容相匹配:
WebMock.StubRequest( ' * ' , ' * ' ).WithBody( ' String content. ' );HTTP请求可以与form-data匹配,并与application/x-www-form-urlencoded的content-type提交。可以组合多个匹配字段值。例如:
WebMock.StubRequest( ' * ' , ' * ' )
.WithFormData( ' AField ' , ' A value. ' )
.WithFormData( ' AOtherField ' , ' Another value. ' );为了简单地匹配一个字段的存在,可以将通配符*传递给该值。
注意:您不能同时匹配Form-Data( WithFormData )和身体内容( WithBody )相匹配。指定两者都会导致最新的呼叫覆盖上一个匹配器。
通过常规表达匹配请求对于涉及资源名称和未知资源ID(例如/resource/999静止资源的动态路线很有用。这样的请求可能是由:
WebMock.StubRequest( ' GET ' , TRegEx.Create( ' ^/resource/d+$ ' ));匹配的标头可以通过以下方式实现。
WebMock.StubRequest( ' * ' , ' * ' )
.WithHeader( ' Accept ' , TRegEx.Create( ' video/.+ ' ));可以执行匹配内容:
WebMock.StubRequest( ' * ' , ' * ' )
.WithBody(TRegEx.Create( ' Hello ' ));可以执行匹配的表单data内容:
WebMock.StubRequest( ' * ' , ' * ' )
.WithFormData( ' AField ' , TRegEx.Create( ' .* ' ));注意:请确保将System.RegularExpressions添加到您的使用条款中。
HTTP请求可以与使用JSON的content-type一起使用application/json使用WithJSON进行匹配。可以组合多个匹配字段值。例如:
WebMock.StubRequest( ' * ' , ' * ' )
.WithJSON( ' ABoolean ' , True)
.WithJSON( ' AFloat ' , 0.123 )
.WithJSON( ' AInteger ' , 1 )
.WithJSON( ' AString ' , ' value ' );第一个论点可以是一条路。例如,在以下JSON中,路径objects[0].key将匹配value 1 。
{
"objects" : [
{ "key" : " value 1 " },
{ "key" : " value 2 " }
]
}注意:可以通过将正则表达式作为第二个参数来匹配字符串模式。例如:
WebMock.StubRequest( ' * ' , ' * ' )
.WithJSON( ' objects[0].key ' , TRegEx.Create( ' valuesd+ ' ));HTTP请求可以与提交的XML数据值匹配。例如:
WebMock.StubRequest( ' * ' , ' * ' )
.WithXML( ' /Object/Attr1 ' , ' Value 1 ' );第一个论点是XPATH表达式。上一个示例将与以下文档进行积极匹配:
<? xml version = " 1.0 " encoding = " UTF-8 " ?>
< Object >
< Attr1 >Value 1</ Attr1 >
</ Object >第二个参数可以是布尔值,浮点,整数或字符串值。
如果要求匹配逻辑比简单匹配更复杂,则可以在测试中提供谓词功能,以允许自定义检查/逻辑以匹配请求。匿名谓词功能将接收一个IWebMockHTTPRequest对象,用于检查请求。如果谓词函数返回True则该存根将被视为匹配,如果返回False则将无法匹配。
具有谓词功能的示例存根:
WebMock.StubRequest(
function(ARequest: IWebMockHTTPRequest): Boolean
begin
Result := True; // Return False to ignore request.
end
);默认情况下,响应状态对于固执请求将是200 OK 。如果在没有注册的存根的情况下向TWebMock提出了请求,则将响应501 Not Implemented 。要指定响应状态ToRespond 。
WebMock.StubRequest( ' GET ' , ' / ' ).ToRespond(TWebMockResponseStatus.NotFound);可以将标题添加到响应存根中,例如:
WebMock.StubRequest( ' * ' , ' * ' )
.ToRespond.WithHeader( ' Header-1 ' , ' Value-1 ' );与请求标头一样,可以通过方法链接或使用WithHeaders方法来指定多个标头的匹配。
WebMock.StubRequest( ' * ' , ' * ' ).ToRespond
.WithHeader( ' Header-1 ' , ' Value-1 ' )
.WithHeader( ' Header-2 ' , ' Value-2 ' );
/* or */
var
Headers: TStringList;
begin
Headers := TStringList.Create;
Headers.Values[ ' Header-1 ' ] := ' Value-1 ' ;
Headers.Values[ ' Header-2 ' ] := ' Value-2 ' ;
WebMock.StubRequest( ' * ' , ' * ' )
.ToRespond.WithHeaders(Headers);
end ;默认情况下,一个固执的响应将返回带有内容类型text/plain零长度主体。可以与WithBody设置的简单响应内容,可以轻松表示为string 。
WebMock.StubRequest( ' GET ' , ' / ' )
.ToRespond.WithBody( ' Text To Return ' );如果要返回特定的内容类型,则可以将其指定为第二个参数
WebMock.StubRequest( ' GET ' , ' / ' )
.ToRespond.WithBody( ' { "status": "ok" } ' , ' application/json ' );当用二进制或大量内容进行固执响应时,将内容作为文件提供可能更容易。可以使用与WithBody具有相同签名的WithBodyFile来实现这一目标,但第一个参数是文件的途径。
WebMock.StubRequest( ' GET ' , ' / ' ).WithBodyFile( ' image.jpg ' ); Delphi-Webmocks将尝试根据文件扩展名设置内容类型。如果文件类型未知,则内容类型将默认为application/octet-stream 。第二个参数可以覆盖内容类型。例如
WebMock.StubRequest( ' GET ' , ' / ' ).WithBodyFile( ' file.myext ' , ' application/xml ' );注意:一个“ cotcha”访问测试中的文件是文件的位置相对于测试可执行文件,默认情况下,使用Windows 32位编译器将输出到Win32Debug文件夹。要正确地引用项目文件夹中的名为Content.txt的文件,路径将为....Content.txt 。
有时,动态响应请求很有用。例如:
WebMock.StubRequest( ' * ' , ' * ' )
.ToRespondWith(
procedure ( const ARequest: IWebMockHTTPRequest;
const AResponse: IWebMockResponseBuilder)
begin
AReponse
.WithStatus( 202 )
.WithHeader( ' header-1 ' , ' a-value ' )
.WithBody( ' Some content... ' );
end
);这可以测试需要对请求进行更深入检查或反映响应中请求的值的功能。例如:
WebMock.StubRequest( ' GET ' , ' /echo_header ' )
.ToRespondWith(
procedure ( const ARequest: IWebMockHTTPRequest;
const AResponse: IWebMockHTTPResponseBuilder)
begin
AResponse.WithHeader( ' my-header ' , ARequest.Headers.Values[ ' my-header ' ]);
end
);在返回成功之前,它也可用于模拟多次尝试的故障。例如:
var LRequestCount := 0 ;
WebMock.StubRequest( ' GET ' , ' /busy_endpoint ' )
.ToRespondWith(
procedure ( const ARequest: IWebMockHTTPRequest;
const AResponse: IWebMockHTTPResponseBuilder)
begin
Inc(LRequestCount);
if LRequestCount < 3 then
AResponse.WithStatus( 408 , ' Request Timeout ' )
else
AResponse.WithStatus( 200 , ' OK ' );
end
);如果您需要清除当前注册的存根,则可以在Twebmock实例上调用ResetStubRegistry或Reset 。常规Reset方法将将Twebmock实例返回到一个空白状态,包括清空存根注册表。较具体的ResetStubRegistry将按建议仅清楚存根注册表。
twebmock实例的每个请求都记录在History属性中。历史记录条目包含所有关键的Web请求信息:方法; requesturi;标题;和身体。
可以根据请求历史记录来撰写断言:
WebClient.Get(WebMock.URLFor( ' document ' ));
Assert.AreEqual( ' GET ' , WebMock.History.Last.Method);
Assert.AreEqual( ' /document ' , WebMock.History.Last.RequestURI);注意:如果您发现自己在此庄园中写断言,应该看一下请求主张,这提供了一种更简洁的方式来定义这些断言。
如果您需要清除请求历史记录,则可以在Twebmock的实例上调用ResetHistory或Reset 。常规Reset方法将将Twebmock实例返回到一个空白状态,包括清空历史记录。较具体的ResetHistory只会清楚地说明历史。
除了使用Dunitx断言验证您的代码外,您还可以使用请求主张检查您是否希望您的代码是否能够按预期执行,以检查是否期望代码执行。
一个简单的请求断言:
WebClient.Get(WebMock.URLFor( ' / ' ));
WebMock.Assert.Get( ' / ' ).WasRequested; // Passes与请求固执一样,您可以通过HTTP方法,URI,查询参数,标头和身体内容(包括使用WithJSON和WithXML )匹配请求。
WebMock.Assert
.Patch( ' /resource`)
.WithQueryParam( ' ParamName ' , ' Value ' )
.WithHeader( ' Content- Type ' , ' application/json ' )
.WithBody( ' { "resource": { "propertyA": "Value" } } ' )
.WasRequested;也可以对任何可以肯定的东西( WasRequested )所主张的任何事情都可以被WasNotRequested地主张。这对于检查您的代码没有执行额外的不需要请求很有用。
该项目遵循语义版本控制。
版权所有©2019-2024 Richard Hatherall [email protected]
WebMocks根据Apache许可证(版本2.0)的条款分发。
有关详细信息,请参见许可证。