How to build an automated API test framework - Part 2
We continue with our API test framework, focusing on payloads and new tests
In the previous post on creating an automated api framework we created a framework and a series of automated checks that will run some basic GET request checks against restful-booker. If you haven't gone through the previous post I would recommend you do so before you continue.
...Welcome back! So let's recap on what we've created so far:
- spec stores our automated checks
- api a library of API endpoints we use in spec to communication with restful-booker
- gemfile / Rakefile manages the running of our framework and it's dependencies
We left the previous post with three checks focused on GET requests but now we want to expand it to cover more than just GET requests. So let's look into how we can extend the framework to create and send a POST request.
Payloads and the builder pattern
Traditionally POST requests require some sort of data that we want to be consumed and actioned upon by our web service / application. For restful-booker it wants a JSON payload in the POST request to /booking so we need to create the ability to generate payloads, which we can do using the builder pattern model.
Erm, What is the builder pattern?
@FriendlyTester has a great blog post on what the builder pattern is and it's context to data creation, which is what we'll be using the pattern for. I would suggest giving it a read before continuing.
Restful-booker takes a 'booking' payload (naturally), so first create a new folder within the root of your project named payloads and within that folder create a new file named booking_payload.rb and paste the following in:
class BookingPayload
attr_accessor :firstname, :lastname, :totalprice, :depositpaid, :checkin, :checkout, :additionalneeds
def initialize(&block)
instance_eval &block if block_given?
end
def toJson
return "{
\"firstname\": \"#{firstname}\",
\"lastname\": \"#{lastname}\",
\"totalprice\": #{totalprice},
\"depositpaid\": #{depositpaid},
\"bookingdates\": {
\"checkin\": \"#{checkin}\",
\"checkout\": \"#{checkout}\"
},
\"additionalneeds\": \"#{additionalneeds}\"
}"
end
end
So how does this builder work?
- The attr_accessor in the class is a shortcut method that allows you to store a variable of say, for example, firstname and create getter and setter methods for it to allow you to update at any point in our checks
- The intialize method is where the magic happens. It allows us to instantiate a new BookingPayload object and the &block allows to set the values we want to set for that object
- Finally toJson takes the values we stored in the payload and generates a stringified version of the booking JSON that we will use to add to our request
And that's it! So let's create that POST /booking check.
Let’s create some checks!
Since we are sending a POST request to the web service we need to create a new method to create the POST request. So we add the following:
def create_booking(payload, content_type)
begin
return RestClient.post 'http://localhost:3001/booking', payload, :accept => :json, :content_type => content_type
rescue => e
return e.response
end
end
Notice how we now add a payload along with additional headers around accept and content_type.
Next, add a link to your new payload builder in spec_helper.rb by adding the following into the file:
require './payloads/booking_payload'
And finally with the booking payload class available to use we can add our new check-in integration_spec.rb
it('POST /booking should return a 200') do
payload = BookingPayload.new do
self.firstname = 'Sally'
self.lastname = 'Jenkins'
self.totalprice = 111
self.depositpaid = true
self.checkin = '11-11-2010'
self.checkout = '12-11-2010'
self.additionalneeds = 'Breakfast'
end
response = Booking.create_booking(payload.toJson, :json)
expect(response.code).to be(200)
end
What is happening here is that in addition to the request and assertion in the check we are creating a BookingPayload specific to the check by instantiating a new BookingPayload and passing values that we want to be added to the payload. We then call payload.toJson when sending the request to create the stringified JSON object that create_booking adds to the request and creates the booking and returns a 200.
Conclusion
So in summary what we've done is:
- Extended our framework to now include a library of payload builders that can be shared across any checks or requests we want to make (For example BookingPayload can be used for both POST and PUT requests)
- Also extended our API library to now include both GET and POST HTTP requests
For the final post in the series, we will look at combining multiple requests in a check for more complex scenarios.