2022-05-31 15:36:54 +03:00
|
|
|
|
# Chaining Requests
|
|
|
|
|
|
|
|
|
|
## Adding Another Request
|
|
|
|
|
|
|
|
|
|
Our basic Hurl file is for the moment:
|
|
|
|
|
|
|
|
|
|
```hurl
|
|
|
|
|
# Checking our home page:
|
|
|
|
|
GET http://localhost:8080
|
|
|
|
|
|
|
|
|
|
HTTP/1.1 200
|
|
|
|
|
[Asserts]
|
|
|
|
|
xpath "string(//head/title)" == "Welcome to Quiz!"
|
|
|
|
|
xpath "//button" count == 2
|
|
|
|
|
xpath "string((//button)[1])" contains "Play"
|
|
|
|
|
xpath "string((//button)[2])" contains "Create"
|
|
|
|
|
# Testing content type:
|
|
|
|
|
header "Content-Type" == "text/html;charset=UTF-8"
|
|
|
|
|
# Testing session cookie:
|
|
|
|
|
cookie "JSESSIONID" exists
|
|
|
|
|
cookie "JSESSIONID[HttpOnly]" exists
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
We're only running one HTTP request and have already added lots of tests on the response. Don't hesitate to add
|
2022-09-28 11:24:24 +03:00
|
|
|
|
many tests, the more asserts you write, the less fragile your tests suite will become.
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
|
|
|
|
Now, we want to perform other HTTP requests and keep adding tests. In the same file, we can simply write another
|
|
|
|
|
request following our first request. Let's say we want to test that we have a [404 page] on a broken link:
|
|
|
|
|
|
2022-10-31 13:50:22 +03:00
|
|
|
|
1. Modify `basic.hurl` to add a second request on a broken URL:
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
|
|
|
|
```hurl
|
|
|
|
|
# Checking our home page:
|
|
|
|
|
GET http://localhost:8080
|
|
|
|
|
|
|
|
|
|
HTTP/1.1 200
|
|
|
|
|
[Asserts]
|
|
|
|
|
xpath "string(//head/title)" == "Welcome to Quiz!"
|
|
|
|
|
xpath "//button" count == 2
|
|
|
|
|
xpath "string((//button)[1])" contains "Play"
|
|
|
|
|
xpath "string((//button)[2])" contains "Create"
|
|
|
|
|
# Testing content type:
|
|
|
|
|
header "Content-Type" == "text/html;charset=UTF-8"
|
|
|
|
|
# Testing session cookie:
|
|
|
|
|
cookie "JSESSIONID" exists
|
|
|
|
|
cookie "JSESSIONID[HttpOnly]" exists
|
|
|
|
|
|
|
|
|
|
# Check that we have a 404 response for broken links:
|
|
|
|
|
GET http://localhost:8080/not-found
|
|
|
|
|
|
|
|
|
|
HTTP/1.1 404
|
|
|
|
|
[Asserts]
|
|
|
|
|
header "Content-Type" == "text/html;charset=UTF-8"
|
|
|
|
|
xpath "string(//h1)" == "Error 404, Page not Found!"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Now, we have two entries in our Hurl file: each entry is composed of one request and one expected response
|
|
|
|
|
description.
|
|
|
|
|
|
2022-09-28 11:24:24 +03:00
|
|
|
|
> In a Hurl file, response descriptions are optional. We could also have written
|
2022-05-31 15:36:54 +03:00
|
|
|
|
> our file with only requests:
|
|
|
|
|
>
|
|
|
|
|
> ```hurl
|
|
|
|
|
> GET http://localhost:8080
|
|
|
|
|
> GET http://localhost:8080/not-found
|
|
|
|
|
> ```
|
|
|
|
|
> But it would have performed nearly zero test. This type of Hurl file can be useful
|
|
|
|
|
> if you use Hurl to get data for instance.
|
|
|
|
|
|
|
|
|
|
2. Run `basic.hurl`:
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ hurl --test basic.hurl
|
2022-08-16 16:30:48 +03:00
|
|
|
|
[1mbasic.hurl[0m: [1;36mRunning[0m [1/1]
|
2022-08-23 19:36:47 +03:00
|
|
|
|
[1mbasic.hurl[0m: [1;32mSuccess[0m (2 request(s) in 12 ms)
|
2022-05-31 15:36:54 +03:00
|
|
|
|
--------------------------------------------------------------------------------
|
2022-08-23 19:36:47 +03:00
|
|
|
|
Executed files: 1
|
|
|
|
|
Succeeded files: 1 (100.0%)
|
|
|
|
|
Failed files: 0 (0.0%)
|
|
|
|
|
Duration: 12 ms
|
2022-05-31 15:36:54 +03:00
|
|
|
|
```
|
|
|
|
|
|
2022-09-28 11:24:24 +03:00
|
|
|
|
We can see that the test is still ok, now two requests are being run in sequence, and each response can be
|
2022-06-11 13:49:24 +03:00
|
|
|
|
tested independently.
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
2022-09-28 11:34:00 +03:00
|
|
|
|
## Test REST API
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
2022-09-28 11:34:00 +03:00
|
|
|
|
So far, we have tested two HTML endpoints. We're going to see now how to test a REST API.
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
|
|
|
|
Our quiz application exposes a health REST resource, available at <http://localhost:8080/api/health>.
|
|
|
|
|
Let's use Hurl to check it.
|
|
|
|
|
|
|
|
|
|
1. In a shell, use Hurl to test the </api/health> endpoint:
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ echo 'GET http://localhost:8080/api/health' | hurl
|
|
|
|
|
{"status":"RUNNING","reportedDate":"2021-06-06T14:08:27Z","healthy":true,"operationId":425276758}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
> Being a classic CLI application, we can use the standard input with Hurl to provide requests
|
|
|
|
|
> to be executed, instead of a file.
|
|
|
|
|
|
2022-09-28 11:34:00 +03:00
|
|
|
|
So, our health API returns this JSON resource:
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
{
|
|
|
|
|
"status": "RUNNING",
|
|
|
|
|
"reportedDate": "2021-06-06T14:08:27Z",
|
|
|
|
|
"healthy": true,
|
|
|
|
|
"operationId": 425276758
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
We can test it with a [JsonPath assert]. JsonPath asserts have the same structure as XPath asserts: a query
|
2022-09-28 11:24:24 +03:00
|
|
|
|
followed by a predicate. A [JsonPath query] is a simple expression to inspect a JSON object.
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
2022-09-28 11:34:00 +03:00
|
|
|
|
2. Modify `basic.hurl` to add a third request that asserts our </api/health> REST API:
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
|
|
|
|
```hurl
|
|
|
|
|
# Checking our home page:
|
|
|
|
|
# ...
|
|
|
|
|
|
|
|
|
|
# Check that we have a 404 response for broken links:
|
|
|
|
|
# ...
|
|
|
|
|
|
2022-09-28 11:34:00 +03:00
|
|
|
|
# Check our health API:
|
2022-05-31 15:36:54 +03:00
|
|
|
|
GET http://localhost:8080/api/health
|
|
|
|
|
|
|
|
|
|
HTTP/1.1 200
|
|
|
|
|
[Asserts]
|
|
|
|
|
header "Content-Type" == "application/json"
|
|
|
|
|
jsonpath "$.status" == "RUNNING"
|
|
|
|
|
jsonpath "$.healthy" == true
|
|
|
|
|
jsonpath "$.operationId" exists
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-28 11:24:24 +03:00
|
|
|
|
Like XPath assert, JsonPath predicate values are typed. String, boolean, number and
|
2022-09-28 11:34:00 +03:00
|
|
|
|
collections are supported. Let's practice writing JsonPath asserts by using another API. In our Quiz model, a
|
2022-05-31 15:36:54 +03:00
|
|
|
|
quiz is a set of questions, and a question resource is exposed through a
|
2022-09-28 11:34:00 +03:00
|
|
|
|
REST API exposed at <http://localhost:8080/api/questions>. We can use it to add checks on getting questions
|
|
|
|
|
through the API endpoint.
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
2022-09-28 11:34:00 +03:00
|
|
|
|
3. Add JSONPath asserts on the </api/questions> REST APIs:
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
|
|
|
|
```hurl
|
|
|
|
|
# Checking our home page:
|
|
|
|
|
# ...
|
|
|
|
|
|
|
|
|
|
# Check that we have a 404 response for broken links:
|
|
|
|
|
# ...
|
|
|
|
|
|
2022-09-28 11:34:00 +03:00
|
|
|
|
# Check our health API:
|
2022-05-31 15:36:54 +03:00
|
|
|
|
# ...
|
|
|
|
|
|
2022-09-28 11:34:00 +03:00
|
|
|
|
# Check question API:
|
2022-05-31 15:36:54 +03:00
|
|
|
|
GET http://localhost:8080/api/questions?offset=0&size=20&sort=oldest
|
|
|
|
|
|
|
|
|
|
HTTP/1.1 200
|
|
|
|
|
[Asserts]
|
|
|
|
|
header "Content-Type" == "application/json"
|
|
|
|
|
jsonpath "$" count == 20
|
|
|
|
|
jsonpath "$[0].id" == "c0d80047"
|
|
|
|
|
jsonpath "$[0].title" == "What is a pennyroyal?"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
> To keep things simple in this tutorial, we have hardcoded mocked data
|
|
|
|
|
> in our Quiz application. That's something you don't want to do when building
|
|
|
|
|
> your application, you want to build an app production ready. A better way to
|
|
|
|
|
> do this should have been to expose a "debug" or "integration" mode on our app
|
2022-09-28 11:24:24 +03:00
|
|
|
|
> defined by environment variables. If our app is launched in "integration" mode,
|
2022-05-31 15:36:54 +03:00
|
|
|
|
> mocked data is used and asserts can be tested on known values. Our app could also use
|
|
|
|
|
> a mocked database, configured in our tests suits.
|
|
|
|
|
|
2022-10-31 13:50:22 +03:00
|
|
|
|
Note that the question API use query parameters `offset`, `size` and `sort`, that's why we have written the URL with
|
2022-05-31 15:36:54 +03:00
|
|
|
|
query parameters <http://localhost:8080/api/questions?offset=0&size=20&sort=oldest>. We can set the query parameters
|
2022-10-31 13:50:22 +03:00
|
|
|
|
in the URL, or use a [query parameter section].
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
|
|
|
|
4. Use a query parameter section in `basic.hurl`:
|
|
|
|
|
|
|
|
|
|
```hurl
|
|
|
|
|
# Checking our home page:
|
|
|
|
|
# ...
|
|
|
|
|
|
|
|
|
|
# Check that we have a 404 response for broken links:
|
|
|
|
|
# ...
|
|
|
|
|
|
2022-09-28 11:34:00 +03:00
|
|
|
|
# Check our health API:
|
2022-05-31 15:36:54 +03:00
|
|
|
|
# ...
|
|
|
|
|
|
2022-09-28 11:34:00 +03:00
|
|
|
|
# Check question API:
|
2022-05-31 15:36:54 +03:00
|
|
|
|
GET http://localhost:8080/api/questions
|
|
|
|
|
[QueryStringParams]
|
|
|
|
|
offset: 0
|
|
|
|
|
size: 20
|
|
|
|
|
sort: oldest
|
|
|
|
|
|
|
|
|
|
HTTP/1.1 200
|
|
|
|
|
[Asserts]
|
|
|
|
|
header "Content-Type" == "application/json"
|
|
|
|
|
jsonpath "$" count == 20
|
|
|
|
|
jsonpath "$[0].id" == "c0d80047"
|
|
|
|
|
jsonpath "$[0].title" == "What is a pennyroyal?"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Finally, our basic Hurl file, with four requests, looks like:
|
|
|
|
|
|
|
|
|
|
```hurl
|
|
|
|
|
# Checking our home page:
|
|
|
|
|
GET http://localhost:8080
|
|
|
|
|
|
|
|
|
|
HTTP/1.1 200
|
|
|
|
|
[Asserts]
|
|
|
|
|
xpath "string(//head/title)" == "Welcome to Quiz!"
|
|
|
|
|
xpath "//button" count == 2
|
|
|
|
|
xpath "string((//button)[1])" contains "Play"
|
|
|
|
|
xpath "string((//button)[2])" contains "Create"
|
|
|
|
|
# Testing content type:
|
|
|
|
|
header "Content-Type" == "text/html;charset=UTF-8"
|
|
|
|
|
# Testing session cookie:
|
|
|
|
|
cookie "JSESSIONID" exists
|
|
|
|
|
cookie "JSESSIONID[HttpOnly]" exists
|
|
|
|
|
|
|
|
|
|
# Check that we have a 404 response for broken links:
|
|
|
|
|
GET http://localhost:8080/not-found
|
|
|
|
|
|
|
|
|
|
HTTP/1.1 404
|
|
|
|
|
[Asserts]
|
|
|
|
|
header "Content-Type" == "text/html;charset=UTF-8"
|
|
|
|
|
xpath "string(//h1)" == "Error 404, Page not Found!"
|
|
|
|
|
|
2022-09-28 11:34:00 +03:00
|
|
|
|
# Check our health API:
|
2022-05-31 15:36:54 +03:00
|
|
|
|
GET http://localhost:8080/api/health
|
|
|
|
|
|
|
|
|
|
HTTP/1.1 200
|
|
|
|
|
[Asserts]
|
|
|
|
|
header "Content-Type" == "application/json"
|
|
|
|
|
jsonpath "$.status" == "RUNNING"
|
|
|
|
|
jsonpath "$.healthy" == true
|
|
|
|
|
jsonpath "$.operationId" exists
|
|
|
|
|
|
2022-09-28 11:34:00 +03:00
|
|
|
|
# Check question API:
|
2022-05-31 15:36:54 +03:00
|
|
|
|
GET http://localhost:8080/api/questions
|
|
|
|
|
[QueryStringParams]
|
|
|
|
|
offset: 0
|
|
|
|
|
size: 20
|
|
|
|
|
sort: oldest
|
|
|
|
|
|
|
|
|
|
HTTP/1.1 200
|
|
|
|
|
[Asserts]
|
|
|
|
|
header "Content-Type" == "application/json"
|
|
|
|
|
jsonpath "$" count == 20
|
|
|
|
|
jsonpath "$[0].id" == "c0d80047"
|
|
|
|
|
jsonpath "$[0].title" == "What is a pennyroyal?"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
5. Run `basic.hurl` and check that every assert of every request has been successful:
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ hurl --test basic.hurl
|
2022-08-16 16:30:48 +03:00
|
|
|
|
[1mbasic.hurl[0m: [1;36mRunning[0m [1/1]
|
2022-08-23 19:36:47 +03:00
|
|
|
|
[1mbasic.hurl[0m: [1;32mSuccess[0m (4 request(s) in 24 ms)
|
2022-05-31 15:36:54 +03:00
|
|
|
|
--------------------------------------------------------------------------------
|
2022-08-23 19:36:47 +03:00
|
|
|
|
Executed files: 1
|
|
|
|
|
Succeeded files: 1 (100.0%)
|
|
|
|
|
Failed files: 0 (0.0%)
|
|
|
|
|
Duration: 31 ms
|
2022-05-31 15:36:54 +03:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Recap
|
|
|
|
|
|
|
|
|
|
We can simply chain requests with Hurl, adding asserts on every response. As your Hurl file will grow,
|
|
|
|
|
don't hesitate to add many comments: your Hurl file will be a valuable and testable documentation
|
|
|
|
|
for your applications.
|
|
|
|
|
|
|
|
|
|
[404 page]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404
|
|
|
|
|
[JsonPath assert]: /docs/asserting-response.md#jsonpath-assert
|
|
|
|
|
[JsonPath query]: https://goessner.net/articles/JsonPath/
|
|
|
|
|
[query parameter section]: /docs/request.md#query-parameters
|
2022-09-02 15:45:54 +03:00
|
|
|
|
[`--test`]: /docs/manual.md#test
|