Библиотека для загрязнения и установки ожиданий по 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 доступен через диспетчер пакетов Embarcadero для Delphi Getit. Если у вас есть недавняя версия Delphi, включая GetIt, это должен быть предпочтительный метод установки.
Веб -мокки должны быть перечислены в Delphinus Package Manager.
Обязательно перезапустите Delphi после установки через Delphinus, иначе единицы не могут быть найдены в ваших тестовых проектах.
Source в «Путь библиотеки» и «Путь просмотра». Если вы хотите, чтобы в Delphi было опубликовано нежное введение в WebMocks для Delphi, в Delphi есть серия статей, начиная с тестирования клиентов HTTP в Delphi с Dunitx и WebMocks.
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 и путь документа. Например, загрязняние глагола HTTP GET до корня сервера / достигается:
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, представленные с помощью content-type application/x-www-form-urlencoded . Многочисленные значения поля соответствующего могут быть объединены. Например:
WebMock.StubRequest( ' * ' , ' * ' )
.WithFormData( ' AField ' , ' A value. ' )
.WithFormData( ' AOtherField ' , ' Another value. ' ); Чтобы просто соответствовать наличию поля, подстановочный знак * может быть передан для значения.
Примечание. Вы не можете совпадать с Data Form ( WithFormData ) и содержанием тела ( WithBody ) одновременно. Указание оба приведет к последнему перезаписи вызова предыдущего маттера.
Сопоставление запроса по регулярной экспрессии может быть полезно для зажара динамических маршрутов для спокойного ресурса, включающего имя ресурса и неизвестный идентификатор ресурса, такой как /resource/999 . Такой запрос может быть загрязнен:
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 для загрязненного запроса. Если запрос сделан в 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 контента. Простой содержимое ответа, которое легко представлено в виде 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» доступа к файлам в тестах является местоположение файла относительно исполняемого тестирования, который по умолчанию с использованием 32-разрядного компилятора Windows будет выводиться в папку 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 будет как предложенное только реестр STUB.
Каждый запрос, сделанный из экземпляра 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 Как и в случае с запросом, вы можете соответствовать запросам с помощью метода HTTP, URI, параметров запроса, заголовков и содержания тела (включая WithJSON и WithXML ).
WebMock.Assert
.Patch( ' /resource`)
.WithQueryParam( ' ParamName ' , ' Value ' )
.WithHeader( ' Content- Type ' , ' application/json ' )
.WithBody( ' { "resource": { "propertyA": "Value" } } ' )
.WasRequested; Все, что может быть заявлено положительно ( WasRequested ), также может быть заявлено негативно с помощью WasNotRequested . Это полезно, чтобы проверить ваш код, не выполняет дополнительные нежелательные запросы.
Этот проект следует за семантической версией.
Copyright © 2019-2024 Richard Hatherall [email protected]
WebMocks распространяется в соответствии с условиями лицензии Apache (версия 2.0).
Смотрите лицензию для деталей.