GithubHelp home page GithubHelp logo

appercept / delphi-webmocks Goto Github PK

View Code? Open in Web Editor NEW
41.0 5.0 11.0 349 KB

Library for stubbing and setting expectations on HTTP requests in Delphi with DUnitX

License: Apache License 2.0

Pascal 100.00%
delphi test-driven-development http http-requests stubbing delphinuspackage

delphi-webmocks's Introduction

Delphi compatibility Platform compatibility License Lines of Code

WebMocks GitHub release (latest by date) GitHub commits since latest release (by SemVer) GitHub Release Date

Library for stubbing and setting expectations on HTTP requests in Delphi with DUnitX.

Requirements

* WebMocks was developed in Delphi 10.3 (Rio) and 10.4 (Sydney) and until version 3.0 was compatible back to XE8. As WebMocks makes use of the System.Net library introduced with XE8 it will not be compatible with earlier versions. Should you require installing on Delphi versions prior to 10.3 you should install version 2.0.0.

Installation: GetIt

WebMocks 3.2.0 is available through Embarcadero's package manager for Delphi GetIt. If you have a recent version of Delphi including GetIt then this should be the preferred installation method.

Installation: Delphinus-Support

WebMocks should now be listed in Delphinus package manager.

Be sure to restart Delphi after installing via Delphinus otherwise the units may not be found in your test projects.

Installation: Manual

  1. Download and extract the latest version 3.2.1.
  2. In "Tools > Options" under the "Language / Delphi / Library" add the extracted Source directory to the "Library path" and "Browsing path".

Getting Started

If you'd like a gentle introduction to WebMocks for Delphi, there is a series of articles published on DEV starting with Testing HTTP clients in Delphi with DUnitX and WebMocks.

Delphi-WebMocks-Demos contains a set of demonstrations to accompany the articles.

Upgrading from versions prior to 2.0.0

Version 2 has dropped the Delphi. namespace from all units. Any projects upgrade to version 2 or later will need to drop the Delphi. prefix from any included WebMocks units.

Setup

In your test unit file a couple of simple steps are required.

  1. Add WebMock to your interface uses.
  2. In your TestFixture class use Setup and TearDown to create/destroy an instance of TWebMock.

Example Unit Test with 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.

By default TWebMock will bind to a port dynamically assigned start at 8080. This behaviour can be overridden by specifying a port at creation.

WebMock := TWebMock.Create(8088);

The use of WebMock.URLFor function within your tests is to simplify constructing a valid URL. The Port property contains the current bound port and BaseURL property contains a valid URL for the server root.

Examples

Stubbing

Request matching by HTTP method and document path

The simplest form of request matching and starting point for all request stubs is by HTTP method and document path. For example stubbing the HTTP verb GET to the server root / is achieved by:

WebMock.StubRequest('GET', '/');

The use of a single wild-card character * can be used to match any request. For example, to match all POST requests regardless of document path you can use:

WebMock.StubRequest('POST', '*');

Similarly, to match any HTTP method for a given path you can use:

WebMock.StubRequest('*', '/path');

It is perfectly possible to have a catch-all of * and * for both HTTP method and document path.

Request matching by header value

HTTP request headers can be matched like:

WebMock.StubRequest('*', '*').WithHeader('Name', 'Value');

Matching multiple headers can be achieved in 2 ways. The first is to simply chain WithHeader calls e.g.:

WebMock.StubRequest('*', '*')
  .WithHeader('Header-1', 'Value-1')
  .WithHeader('Header-2', 'Value-2');

Alternatively, WithHeaders accepts a TStringList of key-value pairs e.g.:

var
  Headers: TStringList;

begin
  Headers := TStringList.Create;
  Headers.Values['Header-1'] := 'Value-1';
  Headers.Values['Header-2'] := 'Value-2';

  WebMock.StubRequest('*', '*').WithHeaders(Headers);
end;

Request matching by header value

HTTP request can be matched by content like:

WebMock.StubRequest('*', '*').WithBody('String content.');

Request matching by form-data

HTTP requests can be matched by form-data as submitted with content-type of application/x-www-form-urlencoded. Multiple matching field values can be combined. For example:

WebMock.StubRequest('*', '*')
  .WithFormData('AField', 'A value.')
  .WithFormData('AOtherField', 'Another value.');

To simply match the presence of a field, a wildcard * can be passed for the value.

NOTE: You cannot match form-data (WithFormData) and body content (WithBody) at the same time. Specifying both will result in the latest call overwriting the previous matcher.

Matching request document path, headers, or content by regular-expression

Matching a request by regular-expression can be useful for stubbing dynamic routes for a ReSTful resource involving a resource name and an unknown resource ID such as /resource/999. Such a request could be stubbed by:

WebMock.StubRequest('GET', TRegEx.Create('^/resource/\d+$'));

Matching headers can similarly by achieved by:

WebMock.StubRequest('*', '*')
  .WithHeader('Accept', TRegEx.Create('video/.+'));

Matching content can be performed like:

WebMock.StubRequest('*', '*')
  .WithBody(TRegEx.Create('Hello'));

Matching form-data content can be performed like:

WebMock.StubRequest('*', '*')
  .WithFormData('AField', TRegEx.Create('.*'));

NOTE: Be sure to add System.RegularExpressions to your uses clause.

Request matching by JSON

HTTP requests can be matched by JSON data as submitted with content-type of application/json using WithJSON. Multiple matching field values can be combined. For example:

WebMock.StubRequest('*', '*')
  .WithJSON('ABoolean', True)
  .WithJSON('AFloat', 0.123)
  .WithJSON('AInteger', 1)
  .WithJSON('AString', 'value');

The first argument can be a path. For example, in the following JSON, the path objects[0].key would match value 1.

{
  "objects": [
    { "key": "value 1" },
    { "key": "value 2" }
  ]
}

NOTE: Strings patterns can be matched by passing a regular expression as the second argument. For example:

WebMock.StubRequest('*', '*')
  .WithJSON('objects[0].key', TRegEx.Create('value\s\d+'));

Request matching by XML

HTTP request can be matched by XML data values submitted. For example:

WebMock.StubRequest('*', '*')
  .WithXML('/Object/Attr1', 'Value 1');

The first argument is an XPath expression. The previous example would make a positive match against the following document:

<?xml version="1.0" encoding="UTF-8"?>
<Object>
  <Attr1>Value 1</Attr1>
</Object>

The second argument can be a boolean, floating point, integer, or string value.

Request matching by predicate function

If matching logic is required to be more complex than the simple matching, a predicate function can be provided in the test to allow custom inspection/logic for matching a request. The anonymous predicate function will receive an IWebMockHTTPRequest object for inspecting the request. If the predicate function returns True then the stub will be regarded as a match, if returning False it will not be matched.

Example stub with predicate function:

WebMock.StubRequest(
  function(ARequest: IWebMockHTTPRequest): Boolean
  begin
    Result := True; // Return False to ignore request.
  end
);

Stubbed Response Codes

By default a response status will be 200 OK for a stubbed request. If a request is made to TWebMock without a registered stub it will respond 501 Not Implemented. To specify the response status use ToRespond.

WebMock.StubRequest('GET', '/').ToRespond(TWebMockResponseStatus.NotFound);

Stubbed Response Headers

Headers can be added to a response stub like:

WebMock.StubRequest('*', '*')
  .ToRespond.WithHeader('Header-1', 'Value-1');

As with request header matching multiple headers can be specified either through method chaining or by using the WithHeaders method.

  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 Response Content: String Values

By default a stubbed response returns a zero length body with content-type text/plain. Simple response content that is easily represented as a string can be set with WithBody.

WebMock.StubRequest('GET', '/')
  .ToRespond.WithBody('Text To Return');

If you want to return a specific content-type it can be specified as the second argument e.g.

WebMock.StubRequest('GET', '/')
  .ToRespond.WithBody('{ "status": "ok" }', 'application/json');

Stubbed Response Content: Fixture Files

When stubbing responses with binary or large content it is likely easier to provide the content as a file. This can be achieved using WithBodyFile which has the same signature as WithBody but the first argument is the path to a file.

WebMock.StubRequest('GET', '/').WithBodyFile('image.jpg');

The Delphi-WebMocks will attempt to set the content-type according to the file extension. If the file type is unknown then the content-type will default to application/octet-stream. The content-type can be overridden with the second argument. e.g.

WebMock.StubRequest('GET', '/').WithBodyFile('file.myext', 'application/xml');

NOTE: One "gotcha" accessing files in tests is the location of the file will be relative to the test executable which, by default, using the Windows 32-bit compiler will be output to the Win32\Debug folder. To correctly reference a file named Content.txt in the project folder, the path will be ..\..\Content.txt.

Dynamic Responses

Sometimes it is useful to dynamically respond to a request. For example:

WebMock.StubRequest('*', '*')
  .ToRespondWith(
    procedure (const ARequest: IWebMockHTTPRequest;
               const AResponse: IWebMockResponseBuilder)
    begin
      AReponse
        .WithStatus(202)
        .WithHeader('header-1', 'a-value')
        .WithBody('Some content...');
    end
  );

This enables testing of features that require deeper inspection of the request or to reflect values from the request back in the response. For example:

WebMock.StubRequest('GET', '/echo_header')
  .ToRespondWith(
    procedure (const ARequest: IWebMockHTTPRequest;
               const AResponse: IWebMockHTTPResponseBuilder)
    begin
      AResponse.WithHeader('my-header', ARequest.Headers.Values['my-header']);
    end
  );

It can also be useful for simulating failures for a number of attempts before returning a success. For example:

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
  );

Resetting Registered Stubs

If you need to clear the current registered stubs you can call ResetStubRegistry or Reset on the instance of TWebMock. The general Reset method will return the TWebMock instance to a blank state including emptying the stub registry. The more specific ResetStubRegistry will as suggested clear only the stub registry.

Request History

Each and every request made of the TWebMock instance is recorded in the History property. History entries contain all the key web request information: Method; RequestURI; Headers; and Body.

It is possible to write assertions based upon the request history e.g.:

WebClient.Get(WebMock.URLFor('document'));

Assert.AreEqual('GET', WebMock.History.Last.Method);
Assert.AreEqual('/document', WebMock.History.Last.RequestURI);

NOTE: Should you find yourself writing assertions in this manor you should take a look at Request Assertions which provides a more concise way of defining these assertions.

Resetting Request History

If you need to clear request history you can call ResetHistory or Reset on the instance of TWebMock. The general Reset method will return the TWebMock instance to a blank state including emptying the history. The more specific ResetHistory will as suggested clear only the history.

Request Assertions

In addition to using DUnitX assertions to validate your code behaved as expected you can also use request assertions to check whether requests you expect your code to perform where executed as expected.

A simple request assertion:

WebClient.Get(WebMock.URLFor('/'));

WebMock.Assert.Get('/').WasRequested; // Passes

As with request stubbing you can match requests by HTTP Method, URI, Query Parameters, Headers, and Body content (including WithJSON and WithXML).

WebMock.Assert
  .Patch('/resource`)
  .WithQueryParam('ParamName', 'Value')
  .WithHeader('Content-Type', 'application/json')
  .WithBody('{ "resource": { "propertyA": "Value" } }')
  .WasRequested;

Negative Assertions

Anything that can be asserted positively (WasRequested) can also be asserted negatively with WasNotRequested. This is useful to check your code is not performing extra unwanted requests.

Development Dependencies (Optional)

  • TestInsight is required to run the Delphi-WebMocks test suite, so, if you're considering contributing and need to run the test suite, install it. If you do TDD in Delphi I would recommend installing and using it in your own projects.

Semantic Versioning

This project follows Semantic Versioning.

License

Copyright ยฉ2019-2024 Richard Hatherall [email protected]

WebMocks is distributed under the terms of the Apache License (Version 2.0).

See LICENSE for details.

delphi-webmocks's People

Contributors

rhatherall avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

delphi-webmocks's Issues

Response headers

It should be possible to specify additional headers on the response e.g.:

WebMock.StubRequest('*', '*')
  .AndReturn.WithHeader('Header1', 'Value1');

As with request header matching multiple headers should be specified either through method chaining or with the WithHeaders method.

WebMock.StubRequest('*', '*')
  .AndReturn.WithHeader('Header1', 'Value1')
  .AndReturn.WithHeader('Header2', 'Value2');

/* or */

var
  Headers: TStringList;
begin
  Headers := TStringList.Create;
  Headers.Values['Header1'] := 'Value1';
  Headers.Values['Header2'] := 'Value2';

  WebMock.StubRequest('*', '*')
    .AndReturn.WithHeaders(Headers);
end;

Add `URLFor` method

The current requirement to use WebMock.BaseURL + 'endpoint' to build a valid URL is untidy. It can lead to confusion as to whether you require a slash (/), which you don't as it's included as part of BaseURL.

The issue becomes more confusing when you see it alongside a client request:

WebMock.StubRequest('GET', '/document'); // `/` is required here

WebClient.Get(WebMock.BaseURL + 'document'); // `/` is not required here

It would be less confusing to have a URLFor method that will take care of joining the URL in the correct manor.

WebMock.StubRequest('GET', '/document');

WebClient.Get(WebMock.URLFor('document'));
{ same as }
WebClient.Get(WebMock.URLFor('/document'));

Only the first WebMock.Assert in a test yields correct assertion result

If a test contains two WebMock.Assert calls, then only the first one yields the correct assertion result. The second one always passes, even if failure would be expected.

Example:

// ... Do a HTTP request (with body 'A') ...
WebMock.Assert.Get('/endpoint').WithBody('A').WasRequested;

// ... Do a second identical HTTP request (again with body 'A') ...
WebMock.Assert.Get('/endpoint').WithBody('B').WasRequested;

The first WebMock.Assert succeeds, but the second one (that checks for B) also succeeds, even though one would expect it to fail.

Request header matching by regular-expressions

Matching request headers should be possible by regular-expression.

Matching a request header by regular-expression should only be possible through an overloaded WithHeader method e.g.:

WebMock.StubRequest('*', '*')
  .WithHeader('Content-Type', TRegEx.Create('audio\/.+'));

TWebMockTestsClient should be replaced with THTTPClient

The WebClient (instance of TWebMockTestsClient) returns TIdHTTPResponse objects. Indy classes are overly complicated as they return properties that are slightly (and sometimes completely) different to HTTP common parlance. Returning a more HTTP generic IHTTPResponse would simplify assertions and remove the requirement for Indy knowledge when testing HTTP responses.

Dynamic request matchers

It may be useful to be able to match a request with more complex logic than typically possible with the standard HTTP field matching. By giving the option to supply an anonymous predicate function it provides the ability to perform any custom/complex request matching logic.

Example usage:

WebMock.StubRequest(
  function(ARequest: IWebMockHTTPRequest): Boolean
  begin
    Result := True; // Return False to ignore request.
  end
);

Dynamic response stubs

It may be useful to be able to respond in a more dynamic way for certain requests. For example if the response needs to reflect something from the request.

Example usage:

WebMock.StubRequest('*', '*').ToRespond.With(
  function(ARequest: IWebMockHTTPRequest): IWebMockHTTPResponse
  begin
    Result := // Do something here to build a response.
  end
);

Plain query parameters (without values) can cause exceptions

When a query parameter has a name but no value, a ERangeError can be raised.

An example URL: http://example.com/?action

At the moment a workaround might be to give it an empty value e.g. ?action= but that's not always possible or desirable.

Request history logging

Every request made of TWebMock should be logged as a historical event. This is a prerequisite of being able to provide assertions/expectations.

Some warnings in Berlin

Hello,
A couple of warnings showed up while compiling with Berlin, these are minor so only the patch file (changed to TXT) is attached.

regards

[dcc64 Warning] Delphi.WebMock.StringRegExMatcher.pas(41): W1010 Method 'ToString' hides virtual method of base type 'TObject'
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.Accepted' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.AlreadyReported' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.BadGateway' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.BadRequest' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.Conflict' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.Created' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.ExpectationFailed' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.FailedDependency' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.Forbidden' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.Found' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.GatewayTimeout' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.Gone' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.VariantAlsoNegotiates' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.HTTPVersionNotSupported' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.ImATeapot' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.IMUsed' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.InsufficientStorage' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.InternalServerError' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.LengthRequired' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.Locked' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.LoopDetected' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.MethodNotAllowed' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.MisdirectedRequest' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.MovedPermanently' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.MultipleChoices' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.MultiStatus' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.NetworkAuthenticationRequired' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.NetworkConnectTimeoutError' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.NoContent' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.NonAuthoritativeInformation' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.NotAcceptable' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.NotExtended' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.NotFound' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.NotImplemented' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.NotModified' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.OK' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.PartialContent' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.PayloadTooLarge' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.PaymentRequired' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.PermanentRedirect' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.PreconditionFailed' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.PreconditionRequired' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.Processing' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.ProxyAuthenticationRequired' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.RequestHeaderFieldsTooLarge' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.RequestedRangeNotSatisfiable' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.RequestTimeout' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.RequestURITooLong' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.ResetContent' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.SeeOther' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.ServiceUnavailable' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.SwitchingProtocols' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.TemporaryRedirect' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.TooManyRequests' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.Unauthorized' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.UnavailableForLegalReasons' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.UnprocessableEntity' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.UnsupportedMediaType' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.UpgradeRequired' with identical parameters will be inacessible from C++
[dcc64 Warning] Delphi.WebMock.ResponseStatus.pas(441): W1029 Duplicate constructor 'TWebMockResponseStatus.UseProxy' with identical parameters will be inacessible from C++
Success
[Warnings.patch.txt](https://github.com/appercept/Delphi-WebMocks/files/3832353/Warnings.patch.txt)


Request path matching by regular-expressions

Matching URIs/paths should be possible by regular-expression.

For example it may be useful to match a dynamic request for a ReSTful route involving a resource name and an unknown resource ID such as /resource/999. Such a request could be stubbed by:

WebMock.StubRequest('GET', TRegEx.Create('^/resource/\d+$'));

Assertion matching by URL query parameters

It would be really useful to be able to make an assertion that matches a URL query parameter. An example assertion would look like:

WebMock.Assert
  .Get('/document')
  .WithQueryParam('ParamName', 'ParamValue')
  .WasRequested;

The usual complement of wildcard * and RegEx matching should be available too.

Delphinus installation needs packages

Each Delphi version needs a package to compile an install for Delphinus support to work. I think packages for 10.3 and 10.4 are required as a minimum.

The History interface does not work as documented

If one want's to access history, one must currenly typecast interface for example like
Assert.AreEqual('/document', IWebMockHTTPRequest (wm.History.Last).RequestURI);
in read.me it says Assert.AreEqual('/document', WebMock.History.Last.RequestURI); which seems to be outdated.

Request content matching by value

Matching requests by content should be possible by value.

For example:

WebMock.StubRequest('*', '*').WithContent('Hello World!');

Request content matching by Form-Data

It would be great to have a DSL capable of conveniently describing and matching form-submitted data.

An example content matcher might look like:

WebMock.StubRequest.WithBody(FormData.WithField('Name', 'Value'));

It might be worth considering matchers that differentiate between application/x-www-form-urlencoded and multipart/form-data allowing more granular matching.

As with the existing matchers, the ability to supply a RegEx pattern should be possible.

Request header matching by value

Matching headers should by possible in two forms. First, a single header could be specified by:

WebMock.StubRequest('GET', '/').WithHeader('X-MyHeader', 'HeaderValue');

This could be chained for multiple headers like so:

WebMock.StubRequest('GET', '/')
  .WithHeader('X-MyHeader-1', 'HeaderValue1')
  .WithHeader('X-MyHeader-2', 'HeaderValue2');

An alternative for many headers could be WithHeaders:

var
  Headers: TStrings;
begin
  Headers := TStringList.Create;
  Headers.Values['X-MyHeader-1'] := 'MyValue1';
  Headers.Values['X-MyHeader-2'] := 'MyValue2';
  Headers.Values['X-MyHeader-3'] := 'MyValue3';
  Headers.Values['X-MyHeader-4'] := 'MyValue4';

  WebMock.StubRequest('GET', '/').WithHeaders(Headers);
end;

Replace usages of Indy HTTP terms with terms used in the RFCs

A number of methods use a language style that differs from general HTTP terms. These methods are not necessarily incorrect but they would be better being in-line with HTTP terminology. For example:

  • ToReturn would be more inline as ToRespond.
  • WithContent should be WithBody as the RFCs refer to "Body" rather than "Content".
  • WithContentFile should be WithBodyFile inline with WithContent.

Packages are not required for Delphinus installation

Delphi packages were added to this project thinking they were required for Delphinus support. They are not but the IDE needs restarting after installation if there are no packages.

Dropping packages that are not required will make project maintenance overheads lower.

Remove `Delphi.` namespace prefix

This is a library for Delphi. It's pretty obvious when you're in Delphi that you're in Delphi so let's not remind people with extra keystrokes.

This will be a breaking change.

Request assertions

When testing components with conditional requests it is useful to assert that a particular request has or has not been made.

Example:

WebMock.Assert.Request('GET', '/').WasRequested;

// or

WebMock.Assert.Request('GET', '/').WasNotRequested;

Convenience methods for the common HTTP verbs GET, PATCH, POST, PUT, and DELETE will help to clean up the readability of the syntax.
Example:

WebMock.Assert.Get('/').WasRequested;

// or

WebMock.Assert.Get('/').WasNotRequested;

As with request matching for stubbing, the HTTP method, URI, headers, and body should be matchable. An example of an assertion predicated on these may look like:

WebMock.Assert.Patch('/resource`)
  .WithHeader('Content-Type', 'application/json')
  .WithBody('{ "resource": { "propertyA": "Value" } }')
  .WasRequested;

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.