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!

History of Software Testing

For my first post of 2016, I’d like to try a non-technical article. I’ve pulled together several sources and compiled this brief history of software testing.

The main source is an article by D. Gelperin and B. Hetzel entitled “The growth of software testing”.1)Gelperin, D.; B. Hetzel (1988). “The Growth of Software Testing”. CACM 31 (6): 687–695. doi:10.1145/62959.62965. ISSN 0001-0782.

Enjoy!

The Debugging-Oriented Period (pre 1956)

In the beginning, testing was focused on the hardware. On September 9, 1947, Grace Hopper (an early computing pioneer known for inventing COBOL) found the first “bug”. A moth was found in the relay of the Harvard Mark II machine. The moth was removed and taped to the page in the log book with the description “1535 Relay #70 Panel F (moth) in relay. First actual case of bug being found”; the first bug report.2)“Bug”, The Jargon File, ver. 4.4.7. Retrieved June 3, 2010.

Testing was not considered a separate part of the SDLC. A developer would “write” a program and then would “check it out” in order to get the bugs out.

In 1950, Alan Turing writes the first article that discusses software testing. The Turing test addresses the question “How would we know that a program exhibits intelligence?” The test consists of an interrogator given the task of interrogating a human player and a computer player and determining which player is which. If the interrogator cannot reliably tell the machine from the human, the machine is said to have passed the test.3)Turing, Alan (October 1950), “Computing Machinery and Intelligence”, Mind LIX (236): 433–460, doi:10.1093/mind/LIX.236.433, ISSN 0026-4423, retrieved 2008-08-18

The Demonstration-Oriented Period (1957 – 1978)

In 1957, Charles Baker distinguishes debugging from testing in a review of Dan McCracken’s book Digital Computer Programming. Program checkout composed of two goals:

  • Debugging “Make sure the program runs”
  • Testing “Make sure the program solves the problem”

Testing during this time would only focus on the happy path.

During this time, employers begin posting job listings explicitly requesting testing skills and software test engineering becomes a career path in a few companies.

The first conference on software testing is held at the University of North Carolina in June of 1972.

The Destruction-Oriented Period (1979-1982)

In 1979, Glenford J. Myers publishes the first software testing book, The Art of Software Testing and defined testing as “the process of executing a program with the intent of finding errors.” and introduces sad path testing.

By using test data that has a high probability of causing program failures, we increase the probability of detecting issues in the program.

During this time the term “testing” became associated with activities other than checkout, such as software analysis and review techniques.

The Evaluation-Oriented Period (1983 – 1987)

In 1983, the Institute for Computer Sciences and Technology of the National Bureau of Standards published Guideline for Lifecycle Validation, Verification, and Testing of Computer Software. This publication describes testing as a methodology which includes analysis, review, and testing activities.

Two national standards are published, one on test documentation (ANSI/IEEE STD 829) in 1983 and one on unit testing (ANSI /IEEE 1008) in 1987.

The Prevention-Oriented Period (1988-present)

Starting in in the late 1980s and continuing to the present, testing becomes focused on preventing program failures before they occur.

The Systematic Test and Evaluation Process (STEP) methodology introduces testing activities being performed in parallel to development activities.

Testing activities begin to include planning, analysis, design, implementation, execution, and maintenance.

It is realized that defect detection during the design phase is significantly cheaper than defect detection at the implementation phase.

Test driven development (TDD) is introduced, raising the veil between tester and programmer. Writing tests first and sharing them with programmers results in code with less bugs.

References   [ + ]

1. Gelperin, D.; B. Hetzel (1988). “The Growth of Software Testing”. CACM 31 (6): 687–695. doi:10.1145/62959.62965. ISSN 0001-0782.
2. “Bug”, The Jargon File, ver. 4.4.7. Retrieved June 3, 2010.
3. Turing, Alan (October 1950), “Computing Machinery and Intelligence”, Mind LIX (236): 433–460, doi:10.1093/mind/LIX.236.433, ISSN 0026-4423, retrieved 2008-08-18