Introduction

So far in this mini series we’ve discussed the [basic usage of the HttpClient Interception library by Just Eat][FirstPost]. We’ve discussed [throwing an exception on missing registrations][SecondPost] and [covered the basics of using bundles][ThirdPost].

In this post we will take a high level view of templating and how it can work in your tests.

How Does Templating Work?

The basics of templating are simple; specify where you would like a templated value to be used and give the keys of which can be specified per test. These keys can also have default values so not all templateable values have to be set each time.

Templated Bundle

Below is an example of a templated bundle file. This provides the same GitHub uri setup as we had previously but with the username in the uri and the username response changeable per test.

{
  "$schema": "https://raw.githubusercontent.com/justeat/httpclient-interception/main/src/HttpClientInterception/Bundles/http-request-bundle-schema.json",
  "id": "example-http-request-bundle",
  "comment": "An example bundle of HTTP requests to be intercepted.",
  "version": 1,
  "items": [
    {
      "id": "with template",
      "comment": "An HTTP request that returns JSON.",
      "uri": "https://api.github.com/users/${username_uri}",
      "method": "GET",
      "status": "200",
      "contentFormat": "json",
      "contentJson": {
        "login": "${username_response}",
        "name": "From Bundle"
      },
      "templateValues": {
        "username_uri": "template",
        "username_response": "from_template"
      }
    }
  ]
}

To indicate a token which can be swapped out it has to be a well named variable and added to the “templateValues” key/value pair section inside the bundle item definition. These templated values can be set to have default values.

For example the above bundle item definition can be used in the following test without any changes to the bundle or code.

[Fact]
public async Task BasicBundle_WithTemplatedDefaults()
{
    // Arrange
    var options = new HttpClientInterceptorOptions().RegisterBundle("basicbundle.json");
    var client = options.CreateHttpClient();

    // Act
    var response = await client.GetStringAsync("https://api.github.com/users/template");

    // Assert
    response.Should().Contain("from_template");
}

As you can see the “template” value in the uri is matched to the “username_uri” variable value which is the default value. And in the response the “username_response” value of “from_template” is returned in the response.

Changing The Values At Runtime

Now we’ve looked at the bundle item which define the templated values and shown how they can be used with the default values we can now look at changing them per test.

To do this we have to setup a regular .NET dictionary to store the new key/value pairs which need to be swapped out at runtime.

var templates = new Dictionary<string, string>
{
    ["username_uri"] = "WestDiscGolf",
    ["username_response"] = "westdiscgolf_all_lowercase"
};

This has to match the specified variable values to allow for the substitution to occur. Once this dictionary has been setup it is then passed into the RegisterBundle method so that the registration code which works with the HttpClientInterceptorOptions instance knows to parse the bundle with the specified template values.

The full test looks like this:

[Fact]
public async Task BasicBundle_WithTemplates()
{
    // Arrange
    var templates = new Dictionary<string, string>
    {
        ["username_uri"] = "WestDiscGolf",
        ["username_response"] = "westdiscgolf_all_lowercase"
    };

    var options = new HttpClientInterceptorOptions().RegisterBundle("basicbundle.json", templates);
    var client = options.CreateHttpClient();

    // Act
    var response = await client.GetStringAsync("https://api.github.com/users/WestDiscGolf");

    // Assert
    response.Should().Contain("westdiscgolf_all_lowercase");
}

As you can see the “username uri” value is now swapped out to the original “WestDiscGolf” request and the response contains the value from the templated values.

Now we have templated values we can change the values as we need using the same base definition. This can be used for testing different urls and responses. There is a limitation on what can be substituted though; only uri segments, request headers, response headers, content headers and the returned content can contain templated values.

Conclusion

In this post we have gone over the high level idea of templating and how you can use a bundle item definition for multiple use cases by swapping out templated values but only defining the base item once.

If you’ve liked this post please check out my [lightning talk][TalkPost] about the basics of the library. In future posts I hope to continue to explore the library more so please subscribe to my rss feed (link below) and reach out to me on Twitter if you have any comments.

[TalkPost]:lightning-talk-on-httpclient-interception-by-just-eat-at-dotnetoxford-apr-2021" >}} [FirstPost]:https://adamstorr.co.uk/blog/beginning-to-make-testing-with-httpclient-easier/ [SecondPost]:https://adamstorr.co.uk/blog/catching-all-the-requests-while-testing-with-httpclient/ [ThirdPost]:https://adamstorr.co.uk/blog/defining-httpclient-test-requests-by-using-a-bundle/