Automated HTTP Testing! Part 1 of 2

When it comes to testing web applications, I’ve talked a lot about running automated tests against the UI via Selenium. While automate UI tests are fun and important, they should be just part of your automated test suite.

AATP

The picture above is an example of the agile automated test pyramid. The agile automated test pyramid describes the quantities of different types of tests in your test suite.

The specific numbers are unimportant. What’s important to understand is that automated UI tests are brittle and difficult to maintain. It’s best to just use automated UI tests to test the happy path of your application. Automated HTTP tests can be used to test more complex scenarios.

To demo how automated HTTP testing works, I’ve created a new C# project, https://github.com/asphaltpanthers/HelloHttp.

There are 5 steps in writing an automated HTTP test:

  1. Identify and build data structures.
  2. Serialize data structures (POST & PUT).
  3. Execute request.
  4. Deserialize response (GET & GETALL).
  5. Execute assertions.

This post deals with executing a test that GETs data from a HTTP service.

Identify and build data structures

HTTP payloads may consist of either JSON or XML. We will use JSON because it is more widely used and easier to serialize and deserialize.

I’ve created some mock HTTP clients via mockable.io. The first one is a GET request that returns a single person.

http://demo3192753.mockable.io/api/person/joe

returns

{
	"firstName":"Joe",
	"lastName": "Schmoe",
	"age": 25,
	"married": false,
	"birthDate": "01/01/1980"
}

From this we can build our C# data structure.

using System;

namespace HelloHttp.Entities
{
    public class Person
    {
        public String FirstName;
        public String LastName;
        public Int32 Age;
        public Boolean Married;
        public DateTime BirthDate;
    }
}

I created this class in a folder named ‘Entities’ in the HelloHttp project.

Execute request and deserialize response

Since we are executing a GET we can skip the serialize step and go directly to executing the request and then deserializing the response.

To send the request we need a new class. I call this one Api and place it at the root of the project.

using System;
using System.Net.Http;

namespace HelloHttp
{
    public static class Api
    {
        public static String Get(Uri uri)
        {
            using (var client = new HttpClient())
            {
                HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, uri);
                HttpResponseMessage response = client.SendAsync(request).Result;
                if (response.IsSuccessStatusCode)
                {
                    return response.Content.ReadAsStringAsync().Result;
                }
                else
                {
                    throw new System.Net.WebException(response.Content.ReadAsStringAsync().Result);
                }
            }
        }
    }
}

This method accepts a URI and sends it over the wire via a HttpClient. Upon success, it returns the result payload as a string. Upon failure, it throws a WebException with the error text as the exception text.

To parameterize the tests so that they can run on different environments, I added an App.config.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="Url" value="http://demo3192753.mockable.io/"/>
  </appSettings>
</configuration>

And I reference this key in a class named Config.cs.

using System;
using System.Configuration;

namespace HelloHttp
{
    public static class Config
    {
        public static readonly String Url = ConfigurationManager.AppSettings["Url"];
    }
}

Next, we’ll add a method to the Person class that executes the GET and deserializes it. This code requires you include the Newtonsoft.Json NuGet package (Json.NET). Json.NET is a third party library that provides JSON serialization and deserialization.

        public static Person GetPerson(string name)
        {
            return JsonConvert.DeserializeObject<Person>(Api.Get(new Uri(Config.Url + "api/person/" + name)));
        }

This method executes a request via the Api class. Remember, the Get method returns a string, so we deserialize it into a Person object.

Execute assertions

There’s one more thing to add to the Person class before finally writing a test.

        public Boolean Equals(Person person)
        {
            return String.Equals(FirstName, person.FirstName) &&
                String.Equals(LastName, person.LastName) &&
                Int32.Equals(Age, person.Age) &&
                Boolean.Equals(Married, person.Married) &&
                DateTime.Equals(BirthDate, person.BirthDate);
        }

Although, not required, this method helps when comparing the actual Person to the expected Person.

We’re now ready to write a test. I created a folder named PredefinedScenarios and added this test.

using HelloHttp.Entities;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Linq;

namespace HelloHttp.PredefinedScenarios
{
    [TestClass]
    public class PersonTests
    {
        [TestMethod]
        public void GetPerson()
        {
            var person = Person.GetPerson("joe");

            Assert.IsTrue(person.Equals(new Person
            {
                FirstName = "Joe",
                LastName = "Schmoe",
                Age = 25,
                Married = false,
                BirthDate = new DateTime(1980, 1, 1)
            }));
        }
    }
}

We first request the person “joe” and then assert that the actual “joe” is equal to the expected “joe”.

Congratulations! You’ve written an automated HTTP test!

Leave a Reply

Your email address will not be published. Required fields are marked *