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".
Top comments (0)