- You know the basics of making REST API requests with Robot Framework.
REST API based testing or RPA can be done in multiple different ways in Robot Framework.
You can of course do this with your own custom Robot Framework library using 3rd-party requests library
or similar, but Robot Framework also has a library for this already:
RESTinstance. It offers
a simple syntax for your REST API based use cases. For example keywords like Get and Post call the API of your choice with
GET and POST requests.
Although in these exercises we'll just validate requests and responses directly, please bear in mind that RESTinstance can be also used to validate and store the JSON schema, making it powerful when you don't need to validate exact response, but rather the properties of the response.
Another way to do it by using the Http keyword from the Browser library.
While the Browser library API handling is much more limited than RESTinstance, if you only have some basic
requests you want to make, it works just fine.
In the exercises we'll test the REST API of the Bad Flask App. The endpoints in the Bad Flask App are:
| Endpoint | Allowed methods |
|---|---|
/api/auth |
POST |
/api/forms |
GET, POST, PUT |
/api/forms/<id> |
GET, PUT |
All the responses are mocked (no data is really changed), so feel free to play around with the endpoints.
💡 You can use
curlcommandline tool to test that the endpoints answer and return something. For example, the command to interact with first endpoint iscurl -X POST http://localhost:5000/api/auth.
- Get the authentication token and set it as a header in suite setup.
- Create 3 separate test cases, which use
Get,Post, andPut. Those tests should be implemented to check the following functions, one per test:- The name of the first form submitter (
/api/forms/1) isJohn Doe. - It's possible to submit a new form. Request body needs to be a JSON string. Response code for a
successful POST is
201. - It's possible to edit an existing form (
/api/forms/1). Request body needs to be a JSON string.
- The name of the first form submitter (
Initialize you test suite
In this exercise we're not going to write very sophisticated Robot Framework code, meaning
that we're going to do very simple test cases without putting keywords in a separate resource file.
In our tests folder, we have a file called api.robot. Let's open that up.
The file resources
../resources/bad_flask_app.robot. That import is only used withBrowserlibrary. If you're doing the exercises withRESTinstance, the import can be safely ignored. Running the suite with an empty resource file will log a warning, but will not affect the outcome of the exercise.
RESTinstance
We're going to use the RESTinstance library, so we need to import REST into our Settings
table. RESTinstance sends requests directly into the target server, so it
requires a URL with the library import to initialize the library to do
queries against that server. We'll test the REST API of the Bad Flask App.
The server is running at http://localhost:5000, so let's initialize the library import with that URL.
- Add a library import for
RESTin yourSettingstable. - Add
http://localhost:5000as an argument for your library import.
Browser
We're going to use the Browser library, so we need to import it into our Settings table in our resource file.
We'll test the REST API of the Bad Flask App. In order to do that, we're going to need a new browser, because
this library uses the browser instance to execute API calls.
From the Browser library documentation we see that there's two possible keywords for this: New Browser and
New Page. New Browser allows us to specify a browser and whether we want to use headless or headful
along with a bunch of other configurations. New Page just opens a new tab in our browser (or calls
New Browser with default parameters if no browser is open) to a URL we
specify. Since we're just using REST API backend, we don't need to see a browser, so we can call New Page
directly.
There's also
Open Browser, but that's only intended to be used for quick debugging and not for production use, so we're not going to use that here.Open Browser(by default) leaves the browser open on failure and opens in headful mode, but it doesn't support really any configurations whereasNew Browsersupports a wide variety of different configuration options.
- Add a library import for
Browserin yourSettingstable tobad_flask_app.robotresource file.- (Optional) Also add the import to your
api.robottest suite file.
- (Optional) Also add the import to your
Let's create ourselves our first keyword and let's call it Open Browser To Our Application. In here, we want
to open our browser to Bad Flask App and verify the page is opened before continuing. We'll use New Page to
open our browser in headless mode. The server is running in http://localhost:5000, so we'll give that
as a parameter to our New Page call.
- Create a new keyword
Open Browser To Our Applicationto your resource file. - Add
New Pagewith the parameterhttp://localhost:5000to your keyword.
To verify the page load is complete, we can use Get Title to assert
the website title is Bad Flask App. Browser library has builtin waiting for all its keywords, so we don't
need to wait for the page elements to load before asserting the title. Browser library support Python-like validations,
so we can use syntax like Get Title == Bad Flask App directly.
As we're also going to use keywords from Browser library directly in our test suite file, it might be useful to also import
Browserthere. It's not strictly necessary and the tests will work just as fine without it. However, it allows you to see the dependencies of your file directly, instead of having to rely on a potentially very long dependency trail. Also, it helps prevent breaking in case of refactoring where resource files and libraries are placed elsewhere.
- Verify that the title is
Bad Flask App.
As we have no other actions to be done before our tests begin we want our browser to open immediately, let's add it as our suite setup in our test suite.
- Add
Open Browser To Our Applicationas yourSuite Setupin your test suite file.
💡 If you're running your server with Docker, you might need to use the Docker-machine's IP address instead of
localhost. You can find the docker-machine IP address by usingdocker inspect <container_name>.
Authenticate and set headers
Before we can query any data from Bad Flask App, we need a way to authenticate to the server.
We only want to authenticate once and use that as the authorization header. This means we
should add this as our Suite Setup in our Settings table.
- Add a keyword
Authenticate And Set Headers. - Add your new keyword as the
Suite Setup.
💡 Suite setup supports only running a single keyword. When doing exercises with
BROWSERlibrary, you need to useRun keywordsas your suite setup to run multiple keywords together. Combine multiple keywords usingAND(full upper-case, e.g.Run Keywords Log 1 AND Log 2).
The endpoint for authentication is /api/auth and it allows only POST requests.
RESTinstance
Inside our Authenticate And Set Headers keyword, we should call the Post keyword to
the authentication endpoint to get the authentication token.
- Use
Postkeyword inside yourAuthenticate And Set Headerswith the/api/authendpoint.
The response is a JSON, and we should be able to get our data from that object. The easiest way
to do this is to use the Output keyword, which logs the request and the response JSONs directly
into the terminal. If we use just Output we notice that our token is inside the body of the
response. We can use standard JSONPath notion $ to match the base of the response body. We can
also match the path by separating each value with a space, so the body of the response would be
response body (name inside the body would be response body name, etc.).
Output also returns the value we search, so if we search for response body (or $) we'll
get just our token as a string. We should store that into a variable. Storing return values into variables
works very much the same way as in any programming language, meaning <variable name>= <variable value>.
Although we need to follow proper Robot Framework syntax for setting variables as well, so setting a variable
requires ${} around the variable name and proper usage of whitespace. For example
${status}= Output response status- Use
Outputto storeresponse bodyinto a variable.
The final thing is to set our headers for the rest of our requests. We'll use Set Headers to
set our token as an authorization bearer header. Set Headers takes as argument a regular JSON string,
se we can just give our token variable as a Bearer to an Authorization key.
- Use
Set Headersto give{ "Authorization": "Bearer ${token}" }as your headers inside yourAuthenticate And Set Headerskeyword.
Note, that
Set Headerssets the headers for the entire suite, so you should avoid using that inside your test cases directly if you want to affect all requests in other test cases. You can add headers directly to request keywords by usingheaders=argument.
Browser
Browser library has
a Http keyword, which allows us to do basic API calls with a body and some headers. Inside our
Authenticate And Set Headers keyword, we should call the Http keyword to the authentication endpoint
by using POST as the method.
- Use
Httpto call/api/authand make aPOSTrequest without a body or headers. Store the return value as a dictionary variable (&{response}).
Http returns JSON as a Python dictionary. The authentication token is the body of our response.
By storing the return value directly as a dictionary object, we can use the much simpler dot notation
for our dictionary ${dict.key.key.key.value} instead of ${dict["key"]["key"]["key"]["value"]}. We can
store our headers as a suite variable, which we can then later use when making other Http requests for
our other exercises. Set a suite variable HEADERS (upper case, since it's a suite variable) and give it the
value {"Authorization": "Bearer ${response.body}"}.
- Use the stored response to set a suite variable with the value
{"Authorization": "Bearer ${response.body}"}.
💡 If you're getting an error
Resolving variable '${response.body}' failed: AttributeError: 'dict' object has no attribute 'body'make sure you're storing our response as&{response}and not as${response}.
💡 The correct access token is indeed
NotAGoodToken, so don't worry if your token looks "funny" - it is intentional.
Get the first form and verify that its poster's name is John Doe.
Now we're ready to create our first test case. We need to use a GET request to get the first form.
We can get it from the endpoint /api/forms/1 and the response is a JSON with the first user's data.
💡 It is possible to test this endpoint with
curl, but since it requires authorization, you will have to add the header to the command too:curl -X GET -H "Authorization: Bearer NotAGoodToken" http://localhost:5000/api/forms/1.
- Create a new test case named
Get First Form And Verify Poster's Identity.
RESTinstance
RESTinstance library keywords are named exactly like the HTTP request methods. This means you can use Get
to make a GET request.
- Use
Getto get the user from the endpoint/api/forms/1.
We can now assert that the queried data is what we expect it to be. We'll use the Output
keyword again to verify our result. Output doesn't verify anything automatically, but
we can query the response body name (or $.name) to get the name of the poster. When we store it in a
variable, we can simply call Should Be Equal to verify that our response is what we expect it
to be. In this case, it's John Doe.
- Use
Outputto storeresponse body nameinto a variable. - Use
Should Be Equalto verify that your variable is equal toJohn Doe.
We've already verified that our user is what we expect it to be. If we didn't want Output
to flood our terminal we could redirect it to a file. Or, we could use String to compare
our result without having to use a variable.
The assertion keywords are always effective on the last query, so you don't need to store the result in a variable nor do we need to query the user again to do our assertion.
- Use
Stringto verifyresponse body nameequals toJohn Doe.
You can also store the return value of
Stringinto a variable. In this case you need to remember that it returns a list, and not a string. So for example the following snippet would resolve in a test failure:Get /api/forms/1 ${a}= Output response body name ${b}= String response body name Should Be Equal ${a} ${b}The output of the test would be
Get First Form And Verify Poster's Identity . "John Doe" Get First Form And Verify Poster's Identity | FAIL | John Doe != ['John Doe']
Browser
Browser uses the Http keyword for all HTTP request methods. As the first argument we need the URL and as the second
argument we need the HTTP request method (GET). We need to
remember to add our headers separately to our Http call.
- Use
Httpto get the user from the endpoint/api/forms/1with theGETmethod. - Use the
${HEADERS}test variable as the request headers. - Store the response into a dictionary variable (
&{response}).
We can now assert that the queried data is what we expect it to be. We can simply use the built in
Should Be Equal keyword to verify our response.body is John Doe.
- Use
Should Be Equalto verify that yourresponse.bodyequalsJohn Doe.
Create a new form using POST and verify it succeeded.
Again, let's create a new test case. This time, need to make a POST request to create a new
form to our website and verify the form creation was successful. The endpoint to create a new form
is /api/forms.
- Create a new test case named
Post New Form And Verify Creation Succeeded.
For our test case, it's enough to specify our form with an id and name. The data is
regular JSON, and it's going to be static, so let's create a variable for that in the
Variables table.
- Create a variable
NEW_FORM_DATAand make it a JSON with anidandnamewith values of your choice.
RESTinstance
As with GET, the RESTinstance keyword for POST is simply Post. We can use our NEW_FORM_DATA variable
as the body for our Post.
The keywords are not case-sensitive, so
post,Post, andPOSTwork equally well.
- Use
Postto the/api/formsendpoint. - Add
NEW_FORM_DATAvariable as a second argument to yourPost.
We still need to verify that our creation was successful. Again, we can use the Output to
get our response and check the response status to see that it's 201. However, this time
the response code is an integer, so we need to use the Should Be Equal As Integers keyword.
Similar to String, we can also directly evaluate the status code with the Integer keyword.
We could also use
${201}inShould Be Equalto verify the response and201are equal integer values.
- Use
Outputto get theresponse statusand store it in a variable. - Use
Should Be Equal As Integersto verify your response is equal to201. - Use
Integerto verify yourresponse statusis equal to201.
Browser
As with GET, we'll use Http as our keyword, but this time we'll just use POST as
the request method. We can add a body to our Http keyword the same way we add headers.
Let's use our NEW_FORM_DATA as the body for our POST request.
- Use
Httpto the/api/formsendpoint and use thePOSTmethod. - Use
HEADERStest variable to set the headers for your request. - Add a
bodyparameter for yourHttpkeyword call and give it the valueNEW_FORM_DATA. - Store the response into a dictionary variable (
&{response}).
We still need to verify that our creation was successful. Again, we've stored the response value
to a dictionary. A successful post has the return code of 201. The response also has an ok key,
which is true if the status code is 200-299. We can use either Should Be Equal As Integers
to verify our response code is 201 or we can use Should Be True to verify response.ok is true.
We could also use
${201}inShould Be Equalto verify the response and201are equal.
- Use
Should Be Equal As Integersto verify your response is equal to201or useShould Be Trueto verifyresponse.ok.
💡 Make the JSON in a single line.
💡 The
idneeds to be unique. The API has 2 forms with ids1and2.
Modify the form form's email address using PUT and verify it succeeded.
It's time for our third test case. This time we're using the PUT method to modify the first form
in the /api/forms/1 endpoint.
We could also use
/api/formsand specify anidin our payload. Either way we do,idis mandatory in either of them. If specified in both, theidspecified by the URL is used.
- Create a new test case named
Modify Form's Email Address And Verify It Succeeded.
We'll only modify the user's email address, so let's create a variable called NEW_EMAIL into our Variables
table. It doesn't really matter what the new email is as long as it's different from the old email. So for example
firstname.lastname@example.com works in this situation.
- Create a variable
NEW_EMAILand make it a JSON withfirstname.lastname@example.comas the value.
RESTinstance
As with the previous exercises, we'll just use Put as our keyword.
- Use
Putfor endpoint/api/forms/1.
First, we need the current email address, so we can compare it to the changed one. Let's use Get to get that. Next,
we'll need a JSON payload for our Put to change the email address.
- Before
Put, add aGetfrom the same endpoint. - Use
StringorOutputto get theresponse body emailand store it inoldvariable. - Add
NEW_EMAILas an argument toPut.
Bad Flask App sends the "modified" form as a response. We can use the response directly
to check if the email is different. We need to store the response body email again into a
variable, and we need to verify the emails are not equal.
- Use
StringourOutputto get the theresponse body emailand store it innewvariable. - Use
Should Not Be Equalto verify thatoldandneware not the same.
💡 It doesn't matter if you use
StringorOutput, but you must use the same afterGetand afterPut.
Browser
It's time for our third test case. This time we're using the PUT method to modify the first form
in the /api/forms/1 endpoint.
- Use
Httpto the/api/forms/1endpoint and use thePUTmethod. - Use
HEADERStest variable to set the headers for your request. - Store the response into a dictionary variable (
&{response}).
First, we need the current email address, so we can compare it to the changed one.
Let's use GET to get that.
Since we're using GET before our actual PUT and we only want the email from that, we can use a
variable with the same name and just overwrite it when we get the PUT response.
- Before
PUT, add aGETfrom the same endpoint. - Use
HEADERStest variable to set the headers for your request. - Store the response into a dictionary variable (
&{response}). - Use
Set Variableto storeresponse.body.emailinto a variable calledold. - Add
NEW_EMAILas the body toPUT. - Store the response into a dictionary variable (
&{response}).
Bad Flask App sends the "modified" form as a response. We can use the response directly
to check if the email is different. We need to store the response.body.email again into a
variable, and we need to verify the emails are not equal.
- Use
Set Variableto storeresponse.body.emailinto a variable callenew. - Use
Should Not Be Equalto verify thatoldandneware not the same.
If you want to check the response status of your
PUTrequest as well, it should be200.