ห้องสมุดสำหรับการตบและตั้งค่าความคาดหวังในคำขอ HTTP ใน Delphi กับ Dunitx
* Webmocks ได้รับการพัฒนาใน Delphi 10.3 (Rio) และ 10.4 (ซิดนีย์) และจนถึงเวอร์ชัน 3.0 เข้ากันได้กับ XE8 ในขณะที่ Webmocks ใช้ประโยชน์จากไลบรารี System.Net ที่แนะนำกับ XE8 มันจะไม่สามารถใช้งานได้กับเวอร์ชันก่อนหน้า หากคุณต้องการการติดตั้งในเวอร์ชัน Delphi ก่อน 10.3 คุณควรติดตั้งเวอร์ชัน 2.0.0
Webmocks 3.2.0 สามารถใช้งานได้ผ่าน Package Manager ของ Embarcadero สำหรับ Delphi GetIt หากคุณมี Delphi เวอร์ชันล่าสุดรวมถึง GETIT นี่ควรเป็นวิธีการติดตั้งที่ต้องการ
ตอนนี้เว็บมอคส์ควรแสดงรายการใน Delphinus Package Manager
ตรวจสอบให้แน่ใจว่าได้รีสตาร์ท Delphi หลังจากติดตั้งผ่าน Delphinus มิฉะนั้นหน่วยอาจไม่พบในโครงการทดสอบของคุณ
Source ที่แยกออกไปใน "เส้นทางไลบรารี" และ "เส้นทางการเรียกดู" หากคุณต้องการการแนะนำอย่างอ่อนโยนเกี่ยวกับเว็บม็อกสำหรับ Delphi มีชุดของบทความที่เผยแพร่บน DEV เริ่มต้นด้วยการทดสอบไคลเอนต์ HTTP ใน Delphi กับ Dunitx และเว็บม็อก
Delphi-Webmocks-Demos มีชุดการสาธิตเพื่อมาพร้อมกับบทความ
เวอร์ชัน 2 ได้ทิ้ง Delphi. เนมสเปซจากทุกหน่วย โครงการใด ๆ ที่อัปเกรดเป็นเวอร์ชัน 2 หรือใหม่กว่าจะต้องวาง Delphi. คำนำหน้าจากหน่วย webmocks ใด ๆ ที่รวมอยู่
ในไฟล์หน่วยทดสอบของคุณจำเป็นต้องมีขั้นตอนง่ายๆสองสามขั้นตอน
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 พฤติกรรมนี้สามารถแทนที่ได้โดยการระบุพอร์ตที่สร้าง
WebMock := TWebMock.Create( 8088 ); การใช้ฟังก์ชั่น WebMock.URLFor ภายในการทดสอบของคุณคือการสร้าง URL ที่ถูกต้อง คุณสมบัติ Port ประกอบด้วยพอร์ตที่ถูกผูกไว้ในปัจจุบันและคุณสมบัติ BaseURL มี URL ที่ถูกต้องสำหรับรูทเซิร์ฟเวอร์
รูปแบบที่ง่ายที่สุดของการจับคู่คำขอและจุดเริ่มต้นสำหรับการร้องขอทั้งหมดเป็นวิธี HTTP และเส้นทางเอกสาร ตัวอย่างเช่น stubbing คำ GET http ไปที่รูทเซิร์ฟเวอร์ / ทำได้โดย:
WebMock.StubRequest( ' GET ' , ' / ' ); การใช้อักขระไวด์การ์ดเดียว * สามารถใช้เพื่อจับคู่คำขอ ใด ๆ ตัวอย่างเช่นเพื่อจับคู่คำขอ POST ทั้งหมดโดยไม่คำนึงถึงเส้นทางเอกสารที่คุณสามารถใช้:
WebMock.StubRequest( ' POST ' , ' * ' );ในทำนองเดียวกันเพื่อให้ตรงกับวิธี HTTP ใด ๆ สำหรับเส้นทางที่กำหนดคุณสามารถใช้:
WebMock.StubRequest( ' * ' , ' /path ' ); เป็นไปได้อย่างสมบูรณ์แบบที่จะมีการจับทั้งหมดของ * และ * สำหรับทั้งวิธี HTTP และเส้นทางเอกสาร
ส่วนหัวคำขอ HTTP สามารถจับคู่ได้เช่น:
WebMock.StubRequest( ' * ' , ' * ' ).WithHeader( ' Name ' , ' Value ' ); การจับคู่หลายส่วนหัวสามารถทำได้ใน 2 วิธี สิ่งแรกคือการโทรแบบเชน 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 ตามที่ส่งมาด้วย content-type ของ application/x-www-form-urlencoded สามารถรวมค่าฟิลด์ที่ตรงกันหลายค่า ตัวอย่างเช่น:
WebMock.StubRequest( ' * ' , ' * ' )
.WithFormData( ' AField ' , ' A value. ' )
.WithFormData( ' AOtherField ' , ' Another value. ' ); เพื่อให้ตรงกับการปรากฏตัวของสนามสามารถส่งผ่าน * ได้สำหรับค่า
หมายเหตุ: คุณไม่สามารถจับคู่ฟอร์ม-ข้อมูล ( WithFormData ) และเนื้อหาของร่างกาย ( WithBody ) ในเวลาเดียวกัน การระบุทั้งสองจะส่งผลให้การโทรล่าสุดเขียนทับตัวจับคู่ก่อนหน้า
การจับคู่คำขอโดยการแสดงออกปกติจะเป็นประโยชน์สำหรับเส้นทางที่มีพลวัตสำหรับทรัพยากรที่เต็มไปด้วยชื่อทรัพยากรและรหัสทรัพยากรที่ไม่รู้จักเช่น /resource/999 คำขอดังกล่าวอาจถูก stubbed โดย:
WebMock.StubRequest( ' GET ' , TRegEx.Create( ' ^/resource/d+$ ' ));ส่วนหัวที่ตรงกันสามารถทำได้โดย: ทำได้โดย:
WebMock.StubRequest( ' * ' , ' * ' )
.WithHeader( ' Accept ' , TRegEx.Create( ' video/.+ ' ));เนื้อหาที่ตรงกันสามารถทำได้เช่น:
WebMock.StubRequest( ' * ' , ' * ' )
.WithBody(TRegEx.Create( ' Hello ' ));เนื้อหารูปแบบการจับคู่สามารถทำได้เช่น:
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 สำหรับคำขอ stubbed หากมีการร้องขอไปยัง 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 ; โดยค่าเริ่มต้นการตอบสนองแบบ stubbed จะส่งคืนร่างกายความยาวเป็นศูนย์พร้อม text/plain เนื้อหาการตอบสนองอย่างง่ายที่แสดงได้ง่ายเป็น string สามารถตั้งค่ากับ WithBody ได้
WebMock.StubRequest( ' GET ' , ' / ' )
.ToRespond.WithBody( ' Text To Return ' );หากคุณต้องการส่งคืนประเภทเนื้อหาเฉพาะสามารถระบุได้ว่าเป็นอาร์กิวเมนต์ที่สองเช่น
WebMock.StubRequest( ' GET ' , ' / ' )
.ToRespond.WithBody( ' { "status": "ok" } ' , ' application/json ' ); เมื่อมีการตอบสนองที่มีเนื้อหาแบบไบนารีหรือขนาดใหญ่มีแนวโน้มที่จะให้เนื้อหาเป็นไฟล์ได้ง่ายขึ้น สิ่งนี้สามารถทำได้โดยใช้กับ WithBodyFile ซึ่งมีลายเซ็นเช่นเดียวกับกับ WithBody แต่อาร์กิวเมนต์แรกคือเส้นทางไปยังไฟล์
WebMock.StubRequest( ' GET ' , ' / ' ).WithBodyFile( ' image.jpg ' ); Delphi-Webmocks จะพยายามตั้งค่าประเภทเนื้อหาตามส่วนขยายไฟล์ หากไม่ทราบประเภทไฟล์ประเภทเนื้อหาจะเริ่มต้นเป็น application/octet-stream ประเภทเนื้อหาสามารถถูกแทนที่ด้วยอาร์กิวเมนต์ที่สอง เช่น
WebMock.StubRequest( ' GET ' , ' / ' ).WithBodyFile( ' file.myext ' , ' application/xml ' ); หมายเหตุ: หนึ่ง "gotcha" การเข้าถึงไฟล์ในการทดสอบคือที่ตั้งของไฟล์จะสัมพันธ์กับการทดสอบที่สามารถเรียกใช้งานได้ซึ่งโดยค่าเริ่มต้นโดยใช้คอมไพเลอร์ Windows 32-bit จะถูกส่งไปยังโฟลเดอร์ 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
); หากคุณต้องการล้างสตับที่ลงทะเบียนในปัจจุบันคุณสามารถเรียก ResetStubRegistry หรือ Reset ในอินสแตนซ์ของ twebmock วิธี Reset ทั่วไปจะส่งคืนอินสแตนซ์ twebmock ไปยังสถานะว่างเปล่ารวมถึงการล้างรีจิสทรีสตับ ResetStubRegistry ที่เฉพาะเจาะจงมากขึ้นจะแนะนำให้ชัดเจนเฉพาะรีจิสทรีสตับ
ทุกคำขอที่ทำจากอินสแตนซ์ Twebmock จะถูกบันทึกไว้ในคุณสมบัติ History รายการประวัติมีข้อมูลคำขอเว็บที่สำคัญทั้งหมด: วิธีการ; คำขอ; ส่วนหัว; และร่างกาย
เป็นไปได้ที่จะเขียนการยืนยันตามประวัติคำขอเช่น:
WebClient.Get(WebMock.URLFor( ' document ' ));
Assert.AreEqual( ' GET ' , WebMock.History.Last.Method);
Assert.AreEqual( ' /document ' , WebMock.History.Last.RequestURI);หมายเหตุ: หากคุณพบว่าตัวเองกำลังเขียนคำยืนยันในคฤหาสน์นี้คุณควรดูการขอยืนยันซึ่งให้วิธีการที่กระชับยิ่งขึ้นในการกำหนดคำยืนยันเหล่านี้
หากคุณต้องการล้างประวัติคำขอคุณสามารถเรียก ResetHistory หรือ Reset ในอินสแตนซ์ของ Twebmock วิธี Reset ทั่วไปจะส่งคืนอินสแตนซ์ twebmock ไปยังสถานะว่างเปล่ารวมถึงการล้างประวัติ ยิ่ง ResetHistory ที่เฉพาะเจาะจงมากขึ้นจะแนะนำให้ชัดเจนเฉพาะประวัติศาสตร์
นอกเหนือจากการใช้การยืนยัน DUNITX เพื่อตรวจสอบรหัสของคุณที่มีพฤติกรรมตามที่คาดไว้คุณยังสามารถใช้การยืนยันการร้องขอเพื่อตรวจสอบว่าคำขอที่คุณคาดหวังว่ารหัสของคุณจะดำเนินการตามที่คาดไว้หรือไม่
การยืนยันการร้องขออย่างง่าย:
WebClient.Get(WebMock.URLFor( ' / ' ));
WebMock.Assert.Get( ' / ' ).WasRequested; // Passes เช่นเดียวกับการร้องขอ Stubbing คุณสามารถจับคู่คำขอด้วยวิธี 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)
ดูใบอนุญาตสำหรับรายละเอียด