使用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)的條款分發。
有關詳細信息,請參見許可證。