Testing RESTful Service in Ruby, Part 2: Run frequently in a Continuous Testing Server
Stabilizing the tests and setting them up in a Continuous Testing server
In Part 1, we create five RESTful service tests in Ruby. They were working, however, but it is not enough.
I hardcoded an ID value (e.g. 66670) to use in some tests, which run fine once only. The ID might already be taken (duplicate error for CREATE), or may not exist (causing READ, UPDATE and DELETE operations to fail). Also, I didn’t clean up after using it.
To run tests in a Continuous Testing (CT) server, I should stabilize the tests firsts: can run them successfully multiple times. In this article, I will revise the tests written in Part 1 and set up running them in a CT server, as regression testing.
Stabilize Test:
1. LIST all records
No changes from Part 1.
it "List all records" do
require "httpclient"
require "rexml"
http = HTTPClient.new
resp = http.get("http://www.thomas-bayer.com/sqlrest/CUSTOMER")
xml_doc = REXML::Document.new(resp.body)
expect(xml_doc.root.elements.size).to be > 10
expect(xml_doc.root.elements.first.text).to eq("1")
end
2. READ one record
No changes, maybe. It might be OK to assume that record #1 always exists, so long as there are no attempts to update or delete it. If this test starts to fail, we can always stabilize this by invoking CREATE service to use the freshly created one (see UPDATE or DELETE for more).
it "Read first record" do
http = HTTPClient.new
customer_id = 1
get_rest_url = "http://www.thomas-bayer.com/sqlrest/CUSTOMER/#{customer_id}"
resp = http.get(get_rest_url)
puts resp.body
expect(resp.body).to include("<LASTNAME>King</LASTNAME>")
end
3. CREATE a new record
If the ID is not provided, the service might assign a unique ID for a newly created record. However, this is not the case for this service. If we supply a specific ID, the first test execution passes, but the second one would fail because of the “duplicate record ID error”. In fact, “500 Internal Server Error” was returned for this scenario.
How can we make sure our creation request passes every time? Supply a unique ID each time the tests are run. This is very easy to do with Ruby Scripting.
To prevent creating duplicate IDs, we need to make this ID dynamic (or random). So I generated a random number between 9001 and 9999 with the Faker gem. I chose this range because it’s can be easily differentiated from real records. There is a ~0.1% chance of duplicate IDs, but that’s a chance I’m willing to take for this exercise.
new_id = Faker::Number.between(from: 9001, to: 9999)
Then use this new_id
in the request data: <ID>#{new_id}</ID>.
After verifying a successful creation, delete this record via invoking DELETE
service.
Full test case
it "Create a new record" do
new_id = Faker::Number.between(from: 9001, to: 9999)http = HTTPClient.new
create_rest_url = "http://www.thomas-bayer.com/sqlrest/CUSTOMER/"
new_record_xml = <<END_OF_MESSAGE
<CUSTOMER>
<ID>#{new_id}</ID>
<FIRSTNAME>P</FIRSTNAME>
<LASTNAME>Sherman</LASTNAME>
<STREET>42 Wallaby Way</STREET>
<CITY>Sydney</CITY>
</CUSTOMER>
END_OF_MESSAGE
resp = http.put(create_rest_url, new_record_xml)
expect(resp.body).not_to include("Internal Server Error")fail_safe { delete_record(new_id) }
end
The delete_record
is a function I created (using TestWise’s Extract to Function refactoring). This is the beauty of scripting automated tests in a power scripting language, such as Ruby. We can use rich and well-implemented libraries, such as Faker (for generating test data), and some basic programming practices, such as Helper Functions and OO design.
4. UPDATE a new record
We cannot just update an existing record in an automated test, because the test won’t be valid after the first run. Check out this article “One Test Automation Scenario Interview Question that Most Candidates Failed”.
Therefore, the test design will be:
- Create a brand new record
- Update it
- Verify the update
- Delete it
it "Update a record" do
record_id = create_recordhttp = HTTPClient.new
update_rest_url = "http://www.thomas-bayer.com/sqlrest/CUSTOMER/#{record_id}/"update_xml = <<END_OF_MESSAGE
<CUSTOMER>
<FIRSTNAME>Paul</FIRSTNAME>
</CUSTOMER>
END_OF_MESSAGE
resp = http.post(update_rest_url, update_xml)
expect(resp.code).to eq(200) # OKexpect(read_record(record_id)).to include("Paul")delete_record(record_id)
end
There are two reusable helper functions I created: create_record
and read_record
.
5. DELETE a record
Like UPDATE, we cannot delete an existing record. Create a new one first.
it "Delete a record" do
record_id = create_recordhttp = HTTPClient.new
delete_rest_url = "http://www.thomas-bayer.com/sqlrest/CUSTOMER/#{record_id}"
resp = http.delete(delete_rest_url)
expect(resp.body).to include("<deleted>#{record_id}</deleted>")
end
Run all tests in TestWise a couple of times
Before I put the tests in a CT Server to run as regression, I will try to run them multiple times locally first. The below video shows the tests running twice in TestWise.
Good, these five tests seem reliable. We are ready to check in and run them on a CT server, such as BuildWise.
Set up build project in BuildWise to run all tests in a CT Server
You can check out this article: “Set Up a Continuous Testing Server to Run Selenium Tests in Minutes” for setting up a new BuildWise CT server.
Once it is up, it is very easy to create a new API Testing project to run these tests. For any CI/CD/CT, we need to check in code/tests into a source repository, Git mostly. If you are not familiar with Git, check out the 10-Minute Guide to Git Version Control for Testers.
The CT build project setup is very easy, it is exactly the as the Selenium one in the guide above. The basic steps are:
1. Check the tests into a git repository
2. Update the Rakefile to run your tests
The project template created by TestWise IDE is BuildWise CT ready. I just need to update Rakefile
to specify the test file to be included in a sequential build (running tests one by one on the server)
3. Set up a build project to run these REST tests in BuildWise.
Screenshot of the project set-up screen:
3. Trigger a few runs
The below is the report of the 2nd run (you can see two runs for this project, top-left).