Article All about HTTP responses with WebMocks in Delphi

In the previous post Testing HTTP clients in Delphi with DUnitX and WebMocks we looked at a DUnitX test’s basic structure using WebMocks to stub an HTTP endpoint. At this point, it appears very simplistic and not at all like many real-world HTTP interactions. Most requests don’t merely respond “OK” with no content. Let’s explore how and what content we can mock, giving your test subjects scenarios allowing you to write tests asserting their behaviour.

The Defaults

When stubbing a request without specifying the response, the default is empty but successful:

HTTP/1.1 200 OK
content-type: text/plain; charset=utf-8
content-length: 0

This response differs from not stubbing the request only in status code which would be 501 Not Implemented. So, how do we customize the response? Using ToRespond is the answer. Let’s take a look at what we can do by chaining a ToRespond call.

Setting Status Codes

The ToRespond function accepts an optional argument AResponseStatus allowing you to specify the response status code. Returning a status of 204 No Content is achieved with:

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

Not all of us have a comprehensive knowledge of HTTP status codes, so, thankfully ToRespond will accept an enum value instead, allowing you to have descriptive symbols in your test code without the need to add comments. Add WebMock.ResponseStatus to your uses clause, and you will be able to refer to status 204 as NoContent:

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

I’ll pause a moment here to point out some small syntactic sugar WebMocks provides: WithStatus. By chaining WithStatus after ToRespond the test becomes more descriptive:

WebMock.StubRequest('GET', '/')
  .ToRespond
  .WithStatus(204);

// or

WebMock.StubRequest('GET', '/')
  .ToRespond
  .WithStatus(NoContent);

Using WithStatus also provides the ability to return a custom status code and text:

WebMock.StubRequest('GET', '/')
  .ToRespond
  .WithStatus(321, 'Back In The Room');

Now we know how to change the response status. Unless you are returning 204 No Content however, you’ll likely want to send some data back too. Let’s take a look at setting the body content next.

Setting Body Content

As we saw earlier, the default response content is a zero-length plain-text response with the UTF-8 character set. Setting body content is done using WithBody:

WebMock.StubRequest('GET', '/')
  .ToRespond
  .WithBody('Hello');

That’s all well and good for plain-text, but you’re quite likely to want to return something else like JSON. That’s easy enough, specify a second argument with a content-type:

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

Now we know how to respond with small snippets of data, what if we need something larger — or not easily represented in text? That’s where WithBodyFile comes in. You provide a path to a file, and WebMocks will attempt to return it with the correct content type.

WebMock.StubRequest('GET', '/video')
  .ToRespond
  .WithBodyFile('video.mp4');

Again, as with WithBody, WithBodyFile accepts a second argument for content-type if the file type is not commonly recognized or ambiguous.

WebMock.StubRequest('GET', '/video')
  .ToRespond
  .WithBodyFile('video.3gp', 'video/3gpp');

Adding Headers

When testing APIs, you will, at some point, need to respond with specific headers. For example in ReST API designs, it is common to return a location header when creating a resource and responding 201 Created:

WebMock.StubRequest('POST', '/resources')
  .ToRespond
  .WithStatus(Created)
  .WithHeader('location', 'http://example.com/resources/1');

You can chain as many calls to WithHeader as you like to build up a list of headers.

WebMock.StubRequest('GET', '/resources/1')
  .ToRespond
  .WithHeader('cache-control', 'no-cache')
  .WithHeader('last-modified', 'Sat, 13 Feb 2021 12:45:26 GMT');

As a convenience, seeing as it is common in Delphi to handle HTTP headers in a TStringList, WebMocks provides WithHeaders accepting a TStrings instance. This allows you to populate a response with headers from an existing list of header values:

var
  Headers: TStringList;
begin
  Headers := TStringList.Create;
  Headers.Values['cache-control'] := 'no-cache';
  Headers.Values['last-modified'] := 'Sat, 13 Feb 2021 12:45:26 GMT';

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

These previous two examples are identical in behaviour. While the call to WithHeaders is a convenient short-hand if you already have a collection of headers built, I believe chaining calls to WithHeader to be more descriptive when writing tests. Ultimately its a preference, you choose what works for you.

What we’ve learnt so far

In the previous article, we saw how easy it is to set up a DUnitX project with WebMocks. In this article, we learnt how to customize HTTP responses with WebMocks using its HTTP-oriented interface methods. Give it a try in your projects and let me know how you get on in the comments or via Twitter. You can find the demo code on GitHub. The next part of this series will be along shortly “Matching HTTP requests with WebMocks in Delphi”.