Extending my solution with API testing capabilities, and troubles with open source projects

In last week’s blog post, I introduced how I would approach creating a solution for creating and executing automated user interface-driven tests. In this blog post, I’m going to extend the capabilities of my solution by adding automated tests that exercise a RESTful API.

Returning readers might know that I’m a big fan of REST Assured. However, since that’s a Java-based DSL, and my solution is written in C#, I can’t use REST Assured. I’ve spent some time looking at alternatives and decided upon using RestSharp for this example. Why RestSharp? Because it has a clean and fairly readable API, which makes it easy to use, even for non-wizardlike programmers, such as yours truly. This is a big plus for me when creating test automation solutions, because, as a consultant, there will always come a moment where you need to hand over your solution to somebody else. And that somebody might be just as inexperienced when it comes to programming as myself, so I think it’s important to use tools that are straightforward and easy to use while still powerful enough to perform the required tasks. RestSharp ticks those boxes fairly well.

A sample feature and scenario for a RESTful API
Again, we start with the top level of our test implementation: the feature and scenario(s) that describe the required behaviour. Here goes:

Feature: CircuitsApi
	In order to impress my friends
	As a Formula 1 fan
	I want to know the number of races for a given Formula 1 season

@api
Scenario Outline: Check the number of races in a season
	Given I want to know the number of Formula One races in <season>
	When I retrieve the circuit list for that season
	Then there should be <numberOfCircuits> circuits in the list returned
	Examples:
	| season | numberOfCircuits |
	| 2017   | 20               |
	| 2016   | 21               |
	| 1966   | 9                |
	| 1950   | 8                |

Note that I’ve added a tag @api to the scenario. This is so I can prevent my solution from starting a browser for these API tests as well (which would just slow down test execution) by writing dedicated setup and teardown methods that execute only for scenarios with a certain tag. This can be done real easy with SpecFlow. See the GitHub page for the solution for more details.

The step definitions
So, how are the above scenario steps implemented? In the Given step, I handle creating the RestClient that is used to send the HTTP request and intercept the response, as well as setting the path parameter specifying the specific year for which I want to check the number of races:

private RestClient restClient;
private RestRequest restRequest;
private IRestResponse restResponse;

[Given(@"I want to know the number of Formula One races in (.*)")]
public void GivenIWantToKnowTheNumberOfFormulaOneRacesIn(string season)
{
    restClient = new RestClient(Constants.ApiBaseUrl); //http://ergast.com/api/f1

    restRequest = new RestRequest("{season}/circuits.json", Method.GET);

    restRequest.AddUrlSegment("season", season);
}

The When step is even more straightforward: all is done here is executing the RestClient and storing the response in the IRestResponse:

[When(@"I retrieve the circuit list for that season")]
public void WhenIRetrieveTheCircuitListForThatSeason()
{
    restResponse = restClient.Execute(restRequest);
}

Finally, in the Then step, we parse the response to get the JSON field value we’re interested in and check whether it matches the expected value. In this case, we’re not interested in a field value, though, but rather in the number of times a field appears in the response (in this case, the length of the array of circuits). And, obviously, we want to report the result of our check to the ExtentReports report we’re creating during test execution:

[Then(@"there should be (.*) circuits in the list returned")]
public void ThenThereShouldBeCircuitsInTheListReturned(int numberOfSeasons)
{
    dynamic jsonResponse = JsonConvert.DeserializeObject(restResponse.Content);

    JArray circuitsArray = jsonResponse.MRData.CircuitTable.Circuits;

    OTAAssert.AssertEquals(null, test, circuitsArray.Count, numberOfSeasons, "The actual number of circuits in the list is equal to the expected value " + numberOfSeasons.ToString());
}

Basically, what we’re doing here is deserializing the JSON response and storing it into a dynamic object. I wasn’t familiar with the dynamic concept before, but it turns out to be very useful here. The dynamic type can be used for objects of which you don’t know the structure until runtime, which holds true here (we don’t know what the JSON response looks like). Then, we can simply traverse the dynamic jsonResponse until we get to the field we need for our check. It may not be the best or most reusable solution, but it definitely shows the power of the C# language here.

The trouble with RestSharp
As you can see, with RestSharp, it’s really easy to write tests for RESTful APIs and add them to our solution. There’s one problem though, and that’s that RestSharp no longer seems to be actively maintained. The most recent version of RestSharp was released on August 26 of 2015, more than a year and a half ago. There’s no response to the issues posted on GitHub, either, which also doesn’t bode very well for the liveliness of the project. For me, when deciding whether or not to use an open source project, this is a big red flag.

One alternative to RestSharp I found was RestAssured.Net. This project looks like an effort to port the original REST Assured to C#. It looks useful enough, however, it suffers from the same problem that RestSharp does: no activity. For me, that’s enough reason to discard it.

Just before writing this post, I was made aware of yet another possible solution, called Flurl. This does look like a promising alternative, but unfortunately I didn’t have the time to try it out for myself before the due date of this blog post. I’ll check it out during the week and if it lives up to its promising appearance, Flurl stands a good chance of being the topic for next week’s blog post. Until then, you can find my RestSharp implementation of the RESTful API tests on the GitHub page of my solution.

Three reasons to start improving your API test automation skills

Modern applications and software development methods have changed the requirements for testers and the skills they need to possess to add real value to their clients and projects. One of these emerging and sought after skills is the ability to design and execute automated tests for APIs. In this post, I will give you three reasons why it might be useful for you to start improving your API test automation skills.

API word cloud

APIs are everywhere
The first reason why you should invest in your API test automation skills is a simple question of demand and supply: APIs are becoming ever more present in current IT solutions. From mobile applications to the Internet of Things, many modern systems and applications expose their data, their business logic or both through APIs. Whether you’re building an application that uses APIs to expose data or logic to the outside world, or you’re on the other side as an API consumer, you need to be able to perform tests on APIs. Otherwise, how are you going to ensure that an API and its integration with the outside world function as expected?

Oh, and if you’re testing an application that consumes a third-party API, please don’t fall into the trap of assuming that this API you’re using works perfectly as designed, or that integration with your own application will be seamless. Can anyone really assure you that that business-critical third party API you’re relying on has been tested for your specific situation and requirements? Thought not. Make sure it does and do some proper testing on it!

API test automation hits the sweet spot between speed and coverage
The second reason why API test automation can be very useful is that automated checks on the API level hit the sweet spot between speed of execution and coverage of application features. Compared to the two other types of automated tests in the test automation pyramid, API-level tests tend to:

  • execute faster than user interface-driven tests. User interface-driven automated tests, such as those written in Selenium WebDriver, need to fire up a browser and render several web pages every time a test is executed. When your tests go through a lot of different pages, execution time skyrockets. API-level tests, on the other hand, have to wait for a server responding to HTTP calls only. The only client-side processing that needs to be done is parsing the response and performing validation checks, for example on specific elements in the response. This is a lot faster than sending (possibly many) HTTP requests to a web server to fetch all objects required for a web page and then waiting until your browser has finished rendering the page.
  • cover more business logic than unit tests. Yes, you should have unit tests. And they should cover as much of the internal workings of your application that make up the business logic. However, there’s only so much unit tests can do. For example, unit tests can check whether the salary of a given employee is calculated correctly in the back end. They cannot guarantee that the same salary is correctly sent out to the front end layer of your application (or to the IRS) upon request, though. For this, you will need to perform tests at a higher level in your application, and API tests are usually perfect for that.

Automated API tests tend to be more reliable
Apart from having the right mixture of speed of execution and coverage of functional aspects, API-level automated checks have another big advantage over user interface-driven automated tests: they’re usually far more reliable. User interface-driven tests constantly have to walk the wobbly rope of synchronization, ever changing (or ‘improving’) user interface designs, dynamic element identification methods, etc. API definitions and interfaces on the other hand are amongst the most stable parts of an application: they follow standardized specification formats (such as WSDL for SOAP or WADL, RAML or Swagger for REST) and once agreed upon, an API does not usually change all that much. This applies especially to outward-facing APIs. For example, Google can easily change its Gmail user interface without this impacting end users (apart from annoyance, maybe, but that’s a different story altogether), but sudden radical changes to its Gmail API would render a lot of third-party applications that have Gmail integration useless. Therefore, changes to an API will usually be a lot fewer and further between, resulting in less maintenance required for your API-level automated tests.

A sample API test in REST Assured

Further reading
I hope this blog post has given you some insight into why I think API test automation skills are a valuable asset for any tester with an interest in test automation. If you want to read more on API testing and test automation, I highly recommend the API Testing Dojo on the SoapUI website. Additionally, you can also check out my other posts on API testing here.

Using the TestNG ITestContext to create smarter REST Assured tests

In this post, I would like to demonstrate two different concepts that I think work very well together:

  • How to store and retrieve data objects using the TestNG ITestContext for better code maintainability
  • How to communicate with RESTful web services that use basic or OAuth2 authorization using REST Assured

Using the PayPal sandbox API as an example, I will show you how you can create readable and maintainable tests for secured APIs using TestNG and REST Assured.

The TestNG ITestContext
If you have a suffficiently large test suite, chances are high that you want to be able to share objects between individual tests to make your tests shorter and easier to maintain. For example, if you are calling a web service multiple times throughout your test suite and that web service requires an authentication token in order to be able to consume it, you might want to request and store that authentication token in the setup phase of your test suite, then retrieve and use it in all subsequent tests where this web service is invoked. This is exactly the scenario we’ll see in this blog post.

TestNG offers a means of storing and retrieving objects between tests through the ITestContext interface. This interface allows you to store (using the inherited setAttribute() method) and retrieve (using getAttribute()) objects. Since the ITestContext is created once and remains active for the duration of your test run, this is the perfect way to implement object sharing in your test suite. Making the ITestContext available in your test methods is easy: just pass it as a parameter to your test method (we’ll see an example further down).

REST Assured authentication options
As you might have read in one of my previous blog posts, REST Assured is a Java library that allows you to write and execute readable tests for RESTful web services. Since we’re talking about secured APIs here, it’s good to know that REST Assured supports the following authentication mechanisms:

  • Basic
  • Digest
  • OAuth (version 1 and 2)
  • Form

In the examples in this post, we’ll take a closer look at both Basic authentication (for requesting an OAuth token) and OAuth2 authentication (for invoking secured web service operations) in REST Assured.

The PayPal sandbox API
To illustrate the concepts introduced above I chose to use the PayPal sandbox API. This is a sandbox version of the ‘live’ PayPal API that can be used to test applications that integrate with PayPal, as well as to goof around. It’s free to use for anybody that has an active PayPal account. You can find all documentation on the API here.

Retrieving an Oauth2 access token
The first step – after creating the necessary test accounts in the sandbox environment – is to construct a call in REST Assured that retrieves an OAuth2 authentication token from the PayPal web service. This request uses basic authentication and looks like this:

@BeforeSuite
public void requestToken(ITestContext context) {

	String response =
			given().
				parameters("grant_type","client_credentials").
				auth().
				preemptive().
				basic("client_id","secret").
			when().
				post("https://api.sandbox.paypal.com/v1/oauth2/token").
				asString();
}

The actual values for client_id and secret are specific to the PayPal sandbox account. Note that we have stored the JSON response as a string. This makes it easier to parse it, as we will see in a moment. The response to this request contains our OAuth2 authentication token:

Our OAuth2 access token

In order to store this token for use in our actual tests, we need to extract it from the response and store it in the TestNG ITestContext:

JsonPath jsonPath = new JsonPath(response);

String accessToken = jsonPath.getString("access_token");
		
context.setAttribute("accessToken", accessToken);

System.out.println("Access token: " + context.getAttribute("accessToken"));

The System.out.println output shows us we have successfully stored the OAuth2 access token in the ITestContext:

Access token has been stored in the ITestContext

Using the OAuth2 access token in your tests
Next, we want to use the previously stored token in subsequent API calls that require OAuth2 authentication. This is fairly straightforward: see for example this test that verifies that no payments have been made for the current test account:

@Test
public void checkNumberOfAssociatedPaymentsIsEqualToZero(ITestContext context) {

	given().
		contentType("application/json").
		auth().
		oauth2(context.getAttribute("accessToken").toString()).
	when().
		get("https://api.sandbox.paypal.com/v1/payments/payment/").
	then().
		assertThat().
		body("count", equalTo(0));
}

Note the use of context.getAttribute() to retrieve the token from the ITestContext. This test passes, which not only tells us that no payments have yet been made by this account, but also that our authentication worked as expected (otherwise, we would have received an authentication error).

Download an example project
The Maven project containing all code from this post can be downloaded here.