2022-05-31 15:36:54 +03:00
|
|
|
|
# Adding Asserts
|
|
|
|
|
|
|
|
|
|
Our basic Hurl file is now:
|
|
|
|
|
|
|
|
|
|
```hurl
|
|
|
|
|
# Our first Hurl file, just checking
|
|
|
|
|
# that our server is up and running.
|
2023-07-21 14:28:26 +03:00
|
|
|
|
GET http://localhost:3000
|
2022-12-19 23:30:08 +03:00
|
|
|
|
HTTP 200
|
2022-05-31 15:36:54 +03:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Currently, we're just checking that our home page is responding with a `200 OK` HTTP status code.
|
|
|
|
|
But we also want to check the _content_ of our home page, to ensure that everything is ok. To check the response
|
|
|
|
|
of an HTTP request with Hurl, we have to _describe_ tests that the response content must pass.
|
|
|
|
|
|
|
|
|
|
To do so, we're going to use [asserts].
|
|
|
|
|
|
2023-07-21 14:28:26 +03:00
|
|
|
|
As our endpoint <http://localhost:3000> is serving HTML content, it makes sense to use [XPath asserts].
|
2022-09-28 11:34:00 +03:00
|
|
|
|
If we want to test a REST API or any sort of API that serves JSON content,
|
2022-05-31 15:36:54 +03:00
|
|
|
|
we could use [JSONPath asserts] instead. There are other type of asserts but every one shares
|
|
|
|
|
the same structure. So, let's look how to write a [XPath asserts].
|
|
|
|
|
|
|
|
|
|
## HTML Body Test
|
|
|
|
|
|
|
|
|
|
### Structure of an assert
|
|
|
|
|
|
|
|
|
|
<div class="schema-container schema-container u-font-size-1 u-font-size-2-sm u-font-size-3-md">
|
|
|
|
|
<div class="schema">
|
|
|
|
|
<span class="schema-token schema-color-2">xpath "string(//h1)"<span class="schema-label">query</span></span>
|
|
|
|
|
<span class="schema-token schema-color-1">contains<span class="schema-label">predicate type</span></span>
|
|
|
|
|
<span class="schema-token schema-color-3">"Hello"<span class="schema-label">predicate value</span></span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
An assert consists of a query and a predicate. As we want to test the value of the HTML title tag, we're
|
|
|
|
|
going to use the [XPath expression] `string(//head/title)`.
|
|
|
|
|
|
2023-07-21 14:28:26 +03:00
|
|
|
|
1. Asserts are written in an `[Asserts]` section, so modify `basic.hurl` file:
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
|
|
|
|
```hurl
|
|
|
|
|
# Our first Hurl file, just checking
|
|
|
|
|
# that our server is up and running.
|
2023-07-21 14:28:26 +03:00
|
|
|
|
GET http://localhost:3000
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
2022-12-19 23:30:08 +03:00
|
|
|
|
HTTP 200
|
2022-05-31 15:36:54 +03:00
|
|
|
|
[Asserts]
|
2023-07-21 14:28:26 +03:00
|
|
|
|
xpath "string(//head/title)" == "Movies Box"
|
2022-05-31 15:36:54 +03:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
2. Run `basic.hurl`:
|
|
|
|
|
|
|
|
|
|
```shell
|
2022-06-11 13:49:24 +03:00
|
|
|
|
$ hurl --test basic.hurl
|
2022-08-16 16:30:48 +03:00
|
|
|
|
[1mbasic.hurl[0m: [1;36mRunning[0m [1/1]
|
2023-07-21 14:28:26 +03:00
|
|
|
|
[1mbasic.hurl[0m: [1;32mSuccess[0m (1 request(s) in 19 ms)
|
2022-06-11 13:49:24 +03:00
|
|
|
|
--------------------------------------------------------------------------------
|
2022-08-23 19:36:47 +03:00
|
|
|
|
Executed files: 1
|
|
|
|
|
Succeeded files: 1 (100.0%)
|
|
|
|
|
Failed files: 0 (0.0%)
|
2023-07-21 14:28:26 +03:00
|
|
|
|
Duration: 19 ms
|
2022-05-31 15:36:54 +03:00
|
|
|
|
```
|
|
|
|
|
|
2022-06-11 13:49:24 +03:00
|
|
|
|
There is no error so everything is good!
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
2023-07-21 14:28:26 +03:00
|
|
|
|
3. Modify the test value to "Movies Bax"
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
|
|
|
|
```hurl
|
|
|
|
|
# Our first Hurl file, just checking
|
|
|
|
|
# that our server is up and running.
|
2023-07-21 14:28:26 +03:00
|
|
|
|
GET http://localhost:3000
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
2022-12-19 23:30:08 +03:00
|
|
|
|
HTTP 200
|
2022-05-31 15:36:54 +03:00
|
|
|
|
[Asserts]
|
2023-07-21 14:28:26 +03:00
|
|
|
|
xpath "string(//head/title)" == "Movies Bax"
|
2022-05-31 15:36:54 +03:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
4. Run `basic.hurl`:
|
|
|
|
|
|
|
|
|
|
```shell
|
2022-06-11 13:49:24 +03:00
|
|
|
|
$ hurl --test basic.hurl
|
2022-08-16 16:30:48 +03:00
|
|
|
|
[1mbasic.hurl[0m: [1;36mRunning[0m [1/1]
|
|
|
|
|
[1;31merror[0m: [1mAssert failure[0m
|
|
|
|
|
[1;34m-->[0m basic.hurl:7:0
|
|
|
|
|
[1;34m|[0m
|
2023-07-21 14:28:26 +03:00
|
|
|
|
[1;34m 7[0m [1;34m|[0m xpath "string(//head/title)" == "Movies Bax"
|
|
|
|
|
[1;34m|[0m [1;31mactual: string <Movies Box>[0m
|
|
|
|
|
[1;34m|[0m [1;31mexpected: string <Movies Bax>[0m
|
2022-08-16 16:30:48 +03:00
|
|
|
|
[1;34m|[0m
|
|
|
|
|
|
2023-07-21 14:28:26 +03:00
|
|
|
|
[1mbasic.hurl[0m: [1;31mFailure[0m (1 request(s) in 22 ms)
|
2022-08-16 16:30:48 +03:00
|
|
|
|
--------------------------------------------------------------------------------
|
2022-08-23 19:36:47 +03:00
|
|
|
|
Executed files: 1
|
|
|
|
|
Succeeded files: 0 (0.0%)
|
|
|
|
|
Failed files: 1 (100.0%)
|
2023-07-21 14:28:26 +03:00
|
|
|
|
Duration: 23 ms
|
2022-05-31 15:36:54 +03:00
|
|
|
|
```
|
|
|
|
|
|
2022-08-16 16:30:48 +03:00
|
|
|
|
Hurl has failed now and provides information on which assert is not valid.
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
|
|
|
|
### Typed predicate
|
|
|
|
|
|
2023-07-21 14:28:26 +03:00
|
|
|
|
Decompose our assert:
|
|
|
|
|
|
|
|
|
|
- __`xpath "string(//head/title)"`__
|
|
|
|
|
is the XPath query
|
|
|
|
|
- __`== "Movies Box"`__
|
|
|
|
|
is our predicate to test the query against
|
|
|
|
|
|
|
|
|
|
You can note that tested values are typed:
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
2023-07-21 14:28:26 +03:00
|
|
|
|
- `xpath "string(//head/title)" ==` __`"true"`__
|
|
|
|
|
tests that the XPath expression is returning a string, and this string is equal to the string `true`
|
|
|
|
|
- `xpath "boolean(//head/title)" ==` __`true`__
|
|
|
|
|
tests that the XPath expression is returning a boolean, and the boolean is `true`
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
|
|
|
|
Some queries can also return collections. For instance, the XPath expression `//button` is returning all the button
|
2023-07-21 14:28:26 +03:00
|
|
|
|
elements present in the [DOM]. We can use it to ensure that we have exactly two `<h3>` tag on our home page,
|
|
|
|
|
with [`count`]:
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
2023-07-21 14:28:26 +03:00
|
|
|
|
1. Add a new assert in `basic.hurl` to check the number of `<h3>` tags:
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
|
|
|
|
```hurl
|
|
|
|
|
# Checking our home page:
|
2023-07-21 14:28:26 +03:00
|
|
|
|
GET http://localhost:3000
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
2022-12-19 23:30:08 +03:00
|
|
|
|
HTTP 200
|
2022-05-31 15:36:54 +03:00
|
|
|
|
[Asserts]
|
2023-07-21 14:28:26 +03:00
|
|
|
|
xpath "string(//head/title)" == "Movies Box"
|
|
|
|
|
xpath "//h3" count == 2
|
2022-05-31 15:36:54 +03:00
|
|
|
|
```
|
|
|
|
|
|
2023-07-21 14:28:26 +03:00
|
|
|
|
2. We can also check each `<h3>`'s content:
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
|
|
|
|
```hurl
|
|
|
|
|
# Checking our home page:
|
2023-07-21 14:28:26 +03:00
|
|
|
|
GET http://localhost:3000
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
2022-12-19 23:30:08 +03:00
|
|
|
|
HTTP 200
|
2022-05-31 15:36:54 +03:00
|
|
|
|
[Asserts]
|
2023-07-21 14:28:26 +03:00
|
|
|
|
xpath "string(//head/title)" == "Movies Box"
|
|
|
|
|
xpath "//h3" count == 2
|
|
|
|
|
xpath "string((//h3)[1])" contains "Popular"
|
|
|
|
|
xpath "string((//h3)[2])" contains "Featured Today"
|
2022-05-31 15:36:54 +03:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
> XPath queries can sometimes be a little tricky to write but modern browsers can help writing these expressions.
|
2023-09-28 20:48:12 +03:00
|
|
|
|
> Try open the JavaScript console of your browser (Firefox, Safari or Chrome) and type `$x("string(//head/title)")`
|
2022-05-31 15:36:54 +03:00
|
|
|
|
> then press Return. You should see the result of your XPath query.
|
|
|
|
|
|
|
|
|
|
3. Run `basic.hurl` and check that every assert has been successful:
|
|
|
|
|
|
|
|
|
|
```shell
|
2022-06-11 13:49:24 +03:00
|
|
|
|
$ 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 (1 request(s) in 11 ms)
|
2022-06-11 13:49:24 +03:00
|
|
|
|
--------------------------------------------------------------------------------
|
2022-08-23 19:36:47 +03:00
|
|
|
|
Executed files: 1
|
|
|
|
|
Succeeded files: 1 (100.0%)
|
|
|
|
|
Failed files: 0 (0.0%)
|
2023-07-21 14:28:26 +03:00
|
|
|
|
Duration: 15 ms
|
2022-05-31 15:36:54 +03:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## HTTP Headers Test
|
|
|
|
|
|
|
|
|
|
We are also going to add tests on the HTTP response headers with explicit [`header` asserts].
|
|
|
|
|
As our endpoint is serving UTF-8 encoded HTML content, we can check the value of the [`Content-Type` response header].
|
|
|
|
|
|
|
|
|
|
1. Add a new assert at the end of `basic.hurl` to test the value of the `Content-Type` HTTP header:
|
|
|
|
|
|
|
|
|
|
```hurl
|
|
|
|
|
# Checking our home page:
|
2023-07-21 14:28:26 +03:00
|
|
|
|
GET http://localhost:3000
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
2022-12-19 23:30:08 +03:00
|
|
|
|
HTTP 200
|
2022-05-31 15:36:54 +03:00
|
|
|
|
[Asserts]
|
2023-07-21 14:28:26 +03:00
|
|
|
|
xpath "string(//head/title)" == "Movies Box"
|
|
|
|
|
xpath "//h3" count == 2
|
|
|
|
|
xpath "string((//h3)[1])" contains "Popular"
|
|
|
|
|
xpath "string((//h3)[2])" contains "Featured Today"
|
2022-05-31 15:36:54 +03:00
|
|
|
|
# Testing HTTP response headers:
|
2023-07-21 14:28:26 +03:00
|
|
|
|
header "Content-Type" == "text/html; charset=utf-8"
|
2022-05-31 15:36:54 +03:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
> Our HTTP response has only one `Content-Type` header, so we're testing this header value as string.
|
|
|
|
|
> The same header could be present multiple times in an HTTP response, with different values.
|
|
|
|
|
> In this case, the `header` query will return collections and could be tested with
|
2023-07-21 14:28:26 +03:00
|
|
|
|
> [`count`] or [`includes`].
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
2022-09-28 11:24:24 +03:00
|
|
|
|
For HTTP headers, we can also use an [implicit header assert]. You can use either implicit or
|
2022-05-31 15:36:54 +03:00
|
|
|
|
explicit header assert: the implicit one allows you to only check the exact value of the header,
|
|
|
|
|
while the explicit one allows you to use other [predicates] (like `contains`, `startsWith`, `matches` etc...).
|
|
|
|
|
|
|
|
|
|
2. Replace the explicit assert with [implicit header assert]:
|
|
|
|
|
|
|
|
|
|
```hurl
|
|
|
|
|
# Checking our home page:
|
2023-07-21 14:28:26 +03:00
|
|
|
|
GET http://localhost:3000
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
2022-12-19 23:30:08 +03:00
|
|
|
|
HTTP 200
|
2022-10-01 11:13:50 +03:00
|
|
|
|
# Implicitly testing response headers:
|
2023-07-21 14:28:26 +03:00
|
|
|
|
Content-Type: text/html; charset=utf-8
|
2022-05-31 15:36:54 +03:00
|
|
|
|
[Asserts]
|
2023-07-21 14:28:26 +03:00
|
|
|
|
xpath "string(//head/title)" == "Movies Box"
|
|
|
|
|
xpath "//h3" count == 2
|
|
|
|
|
xpath "string((//h3)[1])" contains "Popular"
|
|
|
|
|
xpath "string((//h3)[2])" contains "Featured Today"
|
2022-05-31 15:36:54 +03:00
|
|
|
|
```
|
|
|
|
|
|
2023-07-21 14:28:26 +03:00
|
|
|
|
The line
|
|
|
|
|
|
|
|
|
|
`Content-Type: text/html; charset=utf-8`
|
|
|
|
|
|
|
|
|
|
is testing that the header `Content-Type` is present in the response,
|
|
|
|
|
and its value must be exactly `text/html; charset=utf-8`.
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
|
|
|
|
> In the implicit assert, quotes in the header value are part of the value itself.
|
|
|
|
|
|
|
|
|
|
Finally, we want to check that our server is creating a new session.
|
|
|
|
|
|
2023-07-21 14:28:26 +03:00
|
|
|
|
When creating a new session, our Express application returns a [`Set-Cookie` HTTP response header].
|
2022-05-31 15:36:54 +03:00
|
|
|
|
So to test it, we can modify our Hurl file with another header assert.
|
|
|
|
|
|
|
|
|
|
3. Add a header assert on `Set-Cookie` header:
|
|
|
|
|
|
|
|
|
|
```hurl
|
|
|
|
|
# Checking our home page:
|
2023-07-21 14:28:26 +03:00
|
|
|
|
GET http://localhost:3000
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
2022-12-19 23:30:08 +03:00
|
|
|
|
HTTP 200
|
2022-05-31 15:36:54 +03:00
|
|
|
|
[Asserts]
|
2023-07-21 14:28:26 +03:00
|
|
|
|
xpath "string(//head/title)" == "Movies Box"
|
|
|
|
|
xpath "//h3" count == 2
|
|
|
|
|
xpath "string((//h3)[1])" contains "Popular"
|
|
|
|
|
xpath "string((//h3)[2])" contains "Featured Today"
|
2022-05-31 15:36:54 +03:00
|
|
|
|
# Testing HTTP response headers:
|
2023-07-21 14:28:26 +03:00
|
|
|
|
header "Content-Type" == "text/html; charset=utf-8"
|
|
|
|
|
header "Set-Cookie" startsWith "x-session-id="
|
2022-05-31 15:36:54 +03:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
For `Set-Cookie` header, we can use the specialized [Cookie assert].
|
|
|
|
|
Not only we'll be able to easily tests [cookie attributes] (like `HttpOnly`, or `SameSite`), but also
|
|
|
|
|
it simplifies tests on cookies, particularly when there are multiple `Set-Cookie` header in the HTTP response.
|
|
|
|
|
|
|
|
|
|
> Hurl is not a browser, one can see it as syntactic sugar over [curl]. Hurl
|
2023-07-21 14:28:26 +03:00
|
|
|
|
> has no JavaScript runtime and stays close to the HTTP layer. With others tools relying on headless browser, it can be
|
2022-05-31 15:36:54 +03:00
|
|
|
|
> difficult to access some HTTP requests attributes, like `Set-Cookie` header.
|
|
|
|
|
|
2022-09-28 11:24:24 +03:00
|
|
|
|
So to test that our server is responding with a `HttpOnly` session cookie, we can modify our file and add cookie asserts.
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
2023-07-21 14:28:26 +03:00
|
|
|
|
4. Add two cookie asserts on the cookie `x-session-id`:
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
|
|
|
|
```hurl
|
|
|
|
|
# Checking our home page:
|
2023-07-21 14:28:26 +03:00
|
|
|
|
GET http://localhost:3000
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
2022-12-19 23:30:08 +03:00
|
|
|
|
HTTP 200
|
2022-05-31 15:36:54 +03:00
|
|
|
|
[Asserts]
|
2023-07-21 14:28:26 +03:00
|
|
|
|
xpath "string(//head/title)" == "Movies Box"
|
|
|
|
|
xpath "//h3" count == 2
|
|
|
|
|
xpath "string((//h3)[1])" contains "Popular"
|
|
|
|
|
xpath "string((//h3)[2])" contains "Featured Today"
|
|
|
|
|
# Testing HTTP response headers:
|
|
|
|
|
header "Content-Type" == "text/html; charset=utf-8"
|
|
|
|
|
cookie "x-session-id" exists
|
|
|
|
|
cookie "x-session-id[HttpOnly]" exists
|
2022-05-31 15:36:54 +03:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
5. Run `basic.hurl` and check that every assert has been successful:
|
|
|
|
|
|
|
|
|
|
```shell
|
2022-06-11 13:49:24 +03:00
|
|
|
|
$ 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 (1 request(s) in 11 ms)
|
2022-06-11 13:49:24 +03:00
|
|
|
|
--------------------------------------------------------------------------------
|
2022-08-23 19:36:47 +03:00
|
|
|
|
Executed files: 1
|
|
|
|
|
Succeeded files: 1 (100.0%)
|
|
|
|
|
Failed files: 0 (0.0%)
|
2023-07-21 14:28:26 +03:00
|
|
|
|
Duration: 20 ms
|
2022-05-31 15:36:54 +03:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Performance Test
|
|
|
|
|
|
|
|
|
|
> TODO: add duration assert
|
|
|
|
|
|
|
|
|
|
## Recap
|
|
|
|
|
|
|
|
|
|
Our Hurl file is now around 10 lines long, but we're already testing a lot on our home page:
|
|
|
|
|
|
|
|
|
|
- we are testing that our home page is responding with a `200 OK`
|
2023-07-21 14:28:26 +03:00
|
|
|
|
- we are checking the basic structure of our page: a title, 2 `<h3>` tags
|
2022-05-31 15:36:54 +03:00
|
|
|
|
- we are checking that the content type is UTF-8 HTML
|
|
|
|
|
- we are checking that our server has created a session, and that the cookie session has the `HttpOnly` attribute
|
|
|
|
|
|
|
|
|
|
You can see now that launching and running requests with Hurl is fast, _really_ fast.
|
|
|
|
|
|
2022-09-28 11:34:00 +03:00
|
|
|
|
In the next session, we're going to see how we chain request tests, and how we add basic check on a REST API.
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|
|
|
|
|
[asserts]: /docs/asserting-response.md
|
|
|
|
|
[XPath asserts]: /docs/asserting-response.md#xpath-assert
|
|
|
|
|
[JSONPath asserts]: /docs/asserting-response.md#jsonpath-assert
|
|
|
|
|
[XPath expression]: https://en.wikipedia.org/wiki/XPath
|
|
|
|
|
[DOM]: https://en.wikipedia.org/wiki/Document_Object_Model
|
|
|
|
|
[`header` asserts]: /docs/asserting-response.md#header-assert
|
|
|
|
|
[`Content-Type` response header]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type
|
|
|
|
|
[implicit header assert]: /docs/asserting-response.md#headers
|
|
|
|
|
[predicates]: /docs/asserting-response.md#predicates
|
|
|
|
|
[`Set-Cookie` HTTP response header]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#creating_cookies
|
|
|
|
|
[Cookie assert]: /docs/asserting-response.md#cookie-assert
|
|
|
|
|
[cookie attributes]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#attributes
|
|
|
|
|
[curl]: https://curl.se
|
2023-07-21 14:28:26 +03:00
|
|
|
|
[`count`]: /docs/filters.md#count
|
|
|
|
|
[`includes`]: /docs/asserting-response.md#predicates
|
2022-05-31 15:36:54 +03:00
|
|
|
|
|