March 24, 2017

Are you sure the bus line is still listed?: Interacting with the MBTA's API using REST Assured

This is the second part of a blog series on REST APIs and the REST Assured Java library. Did you want to go back to the beginning?

Let's say you are an automation developer on a team developing in Java a new public transportation web application. The customer base for the app is located in Southeastern Massachusetts in the United States.
The MBTA system in Massachusetts controls buses, subways and commuter rail in Boston and its suburbs.
The subway system is collectively known as "The T", as in "Taking the T into Boston" or "The T is running slow... again!" and is divided into the Red, Green, and Orange Line, with a bus route called the Silver Line. 

How can you prove that the #230 bus line, running from the Montello Commuter Rail in Brockton, MA to Braintree MBTA station all the way to Quincy Center Station is still listed in the system?

Are you sure this bus line is still listed in the system? If not, it's a bug.



With the last blog entry, we described why getting this information from the MBTA website <http://www.mbta.com/> would not be the best solution. We also covered how using the MBTA's Application Program Interface (API), MBTA-Realtime <http://realtime.mbta.com/Portal/>, we can easily get the information by:

But how could we turn this into an automated test?

Last year, we talked about how to interact with RESTful APIs with Apache Http Components, or with Postman. With this blog post we are going to use REST Assured, referred to me by automation development consultants Alan Richardson and Baz Dijkstra.

Alan Richardson:

Baz Dijkstra:


What is REST Assured?

REST Assured <https://github.com/rest-assured/rest-assured> was developed as a Java library to handle complex interaction with a REST API simply. According to the REST Assured site at <http://rest-assured.io/>:

"REST Assured is developed and maintained by Johan Haleby with the help of numerous other contributors over the years. Would you like to contribute to the project in any way? Submit a pull request or contact Johan at Twitter.

"Johan started the project when he was working at Jayway back in December of 2010. The project is now sponsored by Parkster". 

Johan is currently a Senior Software Developer and Architect at Parkster AB in Sweden. REST Assured is up to version 3.0.3 released on January 20, 2016.

Need help API Testing with REST Assured? 


On REST Assured's official site <http://rest-assured.io/> it this lists this note, dated June 17, 2016:
  • "Bas Dijkstra has been generous enough to open source his REST Assured workshop. You can read more about this here and you can try out, and contribute to, the exercises available in his github repository".
According to Bas' article, Efficient API testing: How to get started with REST Assured published in TechBeacon (Feb 8, 2017), there are three main reasons for using REST Assured, instead of doing it yourself:
  • "It removes the need for writing a lot of boilerplate code required to set up an HTTP connection, send a request and receive and parse a response
  • "It supports a Given/When/Then test notation, which instantly makes your tests human readable
  • "Since REST Assured is a Java library, integrating it into a continuous integration / continuous delivery setup is a breeze, especially when combined with a Java testing framework such as JUnit or TestNG"

How Could We Interact With MBTA's API With REST Assured?

Testing the application through the API -- and not the web user interface -- means that we can test the system quickly and easily without having to wait for a browser.

We can test that:

... And all these tests can be run in less than three seconds

For these tests, we will be using as resources:
  • Java as the main programming language. 
  • TestNG as the test framework, with its @BeforeClass and @Test annotation to set up pre-conditions for tests and run them. (Want to use JUnit instead? Perfectly fine!)
  • The Java Hamcrest library to use the assertThat keyword. I just think it is more readable. You could use regular assertTrue or assertEqual if you really wanted to. 
  • The Java Utility called TimeUnit to keep track of milliseconds the test runs (see JavaWorld's The Highly Useful Java TimeUnit Enum). 
  • REST Assured to handle interacting with sending and receiving information from the RESTful endpoint.
Feel free to steal and modify this code! I did! When I was playing around with the code in Bas' REST Assured Workshop, I had quite a difficult time trying to modify it to fit the MBTA's API. Thank you so much for the help, Baz! 

The following tests will kick the tires of the API itself, to see if everything is in good working order. 

Validate that, if selected, data will be formatted to JSON:
 import static io.restassured.RestAssured.*;  
 import io.restassured.RestAssured;  
 import static org.hamcrest.Matchers.*;  
 import org.testng.annotations.BeforeClass;  
 import org.testng.annotations.Test;  
 import java.util.concurrent.TimeUnit;  
 /**   
  * Based on https://github.com/basdijkstra/workshops/blob/master/rest-assured/RestAssuredWorkshop  
  *  
  * Need to connect to the MBTA API manually?  
  * http://realtime.mbta.com/developer/api/v2/routes?api_key=wX9NwuHnZU2ToO7GmGR9uw&format=json  
  */  
 public class MbtaRoutesTest {  
   @BeforeClass  
   public static void initPath() {  
     RestAssured.baseURI = "http://realtime.mbta.com/developer/api/v2/";  
   }  
   @Test  
   public void checkResponseContentTypeIsJson() {  
     given().  
         param("api_key","wX9NwuHnZU2ToO7GmGR9uw").  
         param("format","json").  
     when().get("routes").  
     then().assertThat().contentType("application/json");  
   }  

Validate that, if given the correct API key, the HTTP Status Code will be OK: 200
   @Test  
   public void checkResponseCodeIsOk200() {  
     given().  
         param("api_key","wX9NwuHnZU2ToO7GmGR9uw").  
         param("format","json").  
     when().get("routes").  
     then().assertThat().statusCode(200);  
   }  

Validate that, if not entering the correct API Key, the user cannot see data:
   @Test  
   public void checkUserCannotAccessApiWithoutKey() {  
     given().  
         param("api_key","invalid-key").  
         param("format","json").  
     when().get("routes").  
     then().assertThat().statusCode(401);  
   }  

Validate that it will take less than two seconds for data to be returned:
   @Test  
   public void checkResponseTimeIsLessThan2seconds() {  
     given().  
         param("api_key","wX9NwuHnZU2ToO7GmGR9uw").  
         param("format","json").  
     when().get("routes").  
     then().assertThat().time(lessThan(2000L), TimeUnit.MILLISECONDS);  
   }  

Next, we can check that, yes, the 230 Bus Route is still listed.

If we manually go to the URL and have the data processed, it will look like:
 {  
  "mode": [  
   {  
    "route_type": "0",  
    "mode_name": "Subway",  
    "route": [  
     {  
      ...  
     }  
    ]  
   },  
   {  
    "route_type": "1",  
    "mode_name": "Subway",  
    "route": [  
     {  
      "route_id": "Red",  
      "route_name": "Red Line"  
     .....  
     }  
    ]  
   },  
   {  
    "route_type": "2",  
    "mode_name": "Commuter Rail",  
    "route": [  
     {  
      ....  
     }  
    ]  
   },  
   {  
    "route_type": "3",  
    "mode_name": "Bus",  
    "route": [  
     {  
      ....  
     },  
     {  
      "route_id": "230",  
      "route_name": "230"  
     },  
    ]  
   },  
   {  
    "route_type": "4",  
    "mode_name": "Boat",  
    "route": [  
     {  
      "route_id": "Boat-F4",  
      "route_name": "Charlestown Ferry"  
     }
    ]  
   }  
  ]  
 }  


Validate that the 230 Bus Route Is Listed:
   @Test  
   public void checkThatRoute230isListed() {  
     given().  
         param("api_key","wX9NwuHnZU2ToO7GmGR9uw").  
         param("format","json").  
     when().get("routes").  
     then().assertThat().  
         statusCode(200).  
         body("mode.mode_name", hasItems("Bus"),  
             "mode[3].route.route_name", hasItem("230"));;  
   }  

If we run all the tests at the same time, we can see that:

  • All five tests passed!
  • It took less than three seconds for all the tests to run.
  • Yes, Bus #230 is listed!


Once again, thank you very much, Bas, for your help with this!

Happy Testing!


-T.J. Maher
Twitter | LinkedIn | GitHub

// Sr. QA Engineer, Software Engineer in Test, Software Tester since 1996.
// Contributing Writer for TechBeacon.
// "Looking to move away from manual QA? Follow Adventures in Automation on Facebook!"