2022-05-31 15:36:54 +03:00
# Asserting Response
2022-12-19 23:30:08 +03:00
## Asserts
Asserts are used to test various properties of an HTTP response. Asserts can be implicits (such as version, status,
headers) or explicit within an `[Asserts]` section.
```hurl
GET https://api/example.org/cats
HTTP 200
Content-Type: application/json; charset=utf-8 # Implicit assert on Content-Type Hedaer
[Asserts] # Explicit asserts section
bytes count == 120
header "Content-Type" contains "utf-8"
jsonpath "$.cats" count == 49
jsonpath "$.cats[0].name" == "Felix"
jsonpath "$.cats[0].lives" == 9
```
## Implicit asserts
### Version - Status
2022-05-31 15:36:54 +03:00
Expected protocol version and status code of the HTTP response.
Protocol version is one of `HTTP/1.0` , `HTTP/1.1` , `HTTP/2` or
2022-12-19 23:30:08 +03:00
`HTTP` ; `HTTP` describes any version. Note that there are no status text following the status code.
2022-05-31 15:36:54 +03:00
```hurl
GET https://example.org/404.html
2022-12-19 23:30:08 +03:00
HTTP 404
2022-05-31 15:36:54 +03:00
```
2022-12-19 23:30:08 +03:00
Wildcard keywords `HTTP` and `*` can be used to disable tests on protocol version and status:
2022-05-31 15:36:54 +03:00
```hurl
GET https://example.org/api/pets
2022-12-19 23:30:08 +03:00
HTTP *
2022-05-31 15:36:54 +03:00
# Check that response status code is > 400 and <= 500
[Asserts]
status > 400
status < = 500
```
2022-12-19 23:30:08 +03:00
While `HTTP/1.0` , `HTTP/1.1` and `HTTP/2` explicitly check HTTP version:
```hurl
# Check that our server responds with HTTP/2
GET https://example.org/api/pets
HTTP/2 200
```
2022-05-31 15:36:54 +03:00
2022-12-19 23:30:08 +03:00
### Headers
2022-05-31 15:36:54 +03:00
Optional list of the expected HTTP response headers that must be in the received response.
A header consists of a name, followed by a `:` and a value.
For each expected header, the received response headers are checked. If the received header is not equal to the expected,
or not present, an error is raised. Note that the expected headers list is not fully descriptive: headers present in the response
and not in the expected list doesn't raise error.
```hurl
# Check that user toto is redirected to home after login.
POST https://example.org/login
[FormParams]
user: toto
password: 12345678
2022-12-19 23:30:08 +03:00
HTTP 302
2022-05-31 15:36:54 +03:00
Location: https://example.org/home
```
> Quotes in the header value are part of the value itself.
>
> This is used by the [ETag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) Header
> ```
> ETag: W/"<etag_value>"
> ETag: "<etag_value>"
> ```
Testing duplicated headers is also possible.
For example with the `Set-Cookie` header:
```
Set-Cookie: theme=light
Set-Cookie: sessionToken=abc123; Expires=Wed, 09 Jun 2021 10:18:14 GMT
```
You can either test the two header values:
```hurl
GET https://example.org/index.html
Host: example.net
2022-12-19 23:30:08 +03:00
HTTP 200
2022-05-31 15:36:54 +03:00
Set-Cookie: theme=light
Set-Cookie: sessionToken=abc123; Expires=Wed, 09 Jun 2021 10:18:14 GMT
```
Or only one:
```hurl
GET https://example.org/index.html
Host: example.net
2022-12-19 23:30:08 +03:00
HTTP 200
2022-05-31 15:36:54 +03:00
Set-Cookie: theme=light
```
If you want to test specifically the number of headers returned for a given header name, or
if you want to test header value with [predicates] (like `startsWith` , `contains` , `exists` )
you can use the explicit [header assert].
2022-12-19 23:30:08 +03:00
## Explicit asserts
2022-05-31 15:36:54 +03:00
2022-12-19 23:30:08 +03:00
Optional list of assertions on the HTTP response within an `[Asserts]` section. Assertions can describe checks
2022-05-31 15:36:54 +03:00
on status code, on the received body (or part of it) and on response headers.
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" > jsonpath "$.book"< 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" > "Dune"< span class = "schema-label" > predicate value< / span > < / span >
< / div >
< / div >
< 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" > body< span class = "schema-label" > query< / span > < / span >
< span class = "schema-token schema-color-1" > matches< span class = "schema-label" > predicate type< / span > < / span >
< span class = "schema-token schema-color-3" > /\d{4}-\d{2}-\d{2}/< span class = "schema-label" > predicate value</ span ></ span >
< / div >
< / div >
An assert consists of a query followed by a predicate. The format of the query
is shared with [captures], and can be one of :
- [`status` ](#status-assert )
- [`header` ](#header-assert )
2022-10-24 21:58:56 +03:00
- [`url` ](#url-assert )
2022-05-31 15:36:54 +03:00
- [`cookie` ](#cookie-assert )
- [`body` ](#body-assert )
- [`bytes` ](#bytes-assert )
- [`xpath` ](#xpath-assert )
- [`jsonpath` ](#jsonpath-assert )
- [`regex` ](#regex-assert )
- [`sha256` ](#sha-256-assert )
- [`md5` ](#md5-assert )
- [`variable` ](#variable-assert )
- [`duration` ](#duration-assert )
2023-05-03 12:52:31 +03:00
- [`certificate` ](#ssl-certificate-assert )
2022-05-31 15:36:54 +03:00
2022-12-19 23:30:08 +03:00
Queries are used to extract data from the HTTP response. Queries, in asserts and in captures, can be refined with [filters], like
[`count`][count] to add tests on collections sizes.
2022-05-31 15:36:54 +03:00
### Predicates
2022-12-19 23:30:08 +03:00
Predicates consist of a predicate function and a predicate value. Predicate functions are:
| Predicate | Description | Example |
|--------------------|-------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------|
| __ `==` __ | Query and predicate value are equals | `jsonpath "$.book" == "Dune"` |
| __ `!=` __ | Query and predicate value are different | `jsonpath "$.color" != "red"` |
| __ `>` __ | Query number is greater than predicate value | `jsonpath "$.year" > 1978` |
| __ `>=` __ | Query number is greater than or equal to the predicate value | `jsonpath "$.year" >= 1978` |
| __ `<` __ | Query number is less than that predicate value | `jsonpath "$.year" < 1978` |
| __ `<=` __ | Query number is less than or equal to the predicate value | `jsonpath "$.year" <= 1978` |
| __ `startsWith` __ | Query starts with the predicate value< br > Value is string or a binary content | `jsonpath "$.movie" startsWith "The"` < br >< br > `bytes startsWith hex,efbbbf;` |
| __ `endsWith` __ | Query ends with the predicate value< br > Value is string or a binary content | `jsonpath "$.movie" endsWith "Back"` < br >< br > `bytes endsWith hex,ab23456;` |
| __ `contains` __ | Query contains the predicate value< br > Value is string or a binary content | `jsonpath "$.movie" contains "Empire"` < br >< br > `bytes contains hex,beef;` |
| __ `includes` __ | Query collections includes the predicate value | `jsonpath "$.nooks" includes "Dune"` |
| __ `matches` __ | Part of the query string matches the regex pattern described by the predicate value | `jsonpath "$.release" matches "\\d{4}"` < br >< br > `jsonpath "$.release" matches /\d{4}/` |
| __ `exists` __ | Query returns a value | `jsonpath "$.book" exists` |
2023-04-17 19:05:07 +03:00
| __ `isEmpty` __ | Query returns an empty collection | `jsonpath "$.movies" isEmpty` |
2022-12-19 23:30:08 +03:00
| __ `isInteger` __ | Query returns an integer | `jsonpath "$.count" isInteger` |
| __ `isFloat` __ | Query returns a float | `jsonpath "$.height" isFloat` |
2023-04-17 19:05:07 +03:00
| __ `isBoolean` __ | Query returns a boolean | `jsonpath "$.succeeded" isBoolean` |
2022-12-19 23:30:08 +03:00
| __ `isString` __ | Query returns a string | `jsonpath "$.name" isString` |
| __ `isCollection` __ | Query returns a collection | `jsonpath "$.books" isCollection` |
2022-05-31 15:36:54 +03:00
Each predicate can be negated by prefixing it with `not` (for instance, `not contains` or `not exists` )
< 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" > jsonpath "$.book"< span class = "schema-label" > query< / span > < / span >
< span class = "schema-token schema-color-1" > not contains< span class = "schema-label" > predicate type< / span > < / span >
< span class = "schema-token schema-color-3" > "Dune"< span class = "schema-label" > predicate value< / span > < / span >
< / div >
< / div >
2022-12-19 23:30:08 +03:00
A predicate value is typed, and can be a string, a boolean, a number, a bytestream, `null` or a collection. Note that
2022-05-31 15:36:54 +03:00
`"true"` is a string, whereas `true` is a boolean.
For instance, to test the presence of a h1 node in an HTML response, the following assert can be used:
```hurl
GET https://example.org/home
2022-12-19 23:30:08 +03:00
HTTP 200
2022-05-31 15:36:54 +03:00
[Asserts]
xpath "boolean(count(//h1))" == true
xpath "//h1" exists # Equivalent but simpler
```
As the XPath query `boolean(count(//h1))` returns a boolean, the predicate value in the assert must be either
`true` or `false` without double quotes. On the other side, say you have an article node and you want to check the value of some
[data attributes]:
```xml
< article
id="electric-cars"
data-visible="true"
...
< / article >
```
The following assert will check the value of the `data-visible` attribute:
```hurl
GET https://example.org/home
2022-12-19 23:30:08 +03:00
HTTP 200
2022-05-31 15:36:54 +03:00
[Asserts]
xpath "string(//article/@data-visible)" == "true"
```
In this case, the XPath query `string(//article/@data-visible)` returns a string, so the predicate value must be a
string.
2022-12-19 23:30:08 +03:00
The predicate function `equals` can be used with string, numbers or booleans; `startWith` and `contains` can only
be used with strings and bytes, while `matches` only works on string. If a query returns a number, using a `matches` predicate will cause a runner error.
2022-05-31 15:36:54 +03:00
```hurl
# A really well tested web page...
GET https://example.org/home
2022-12-19 23:30:08 +03:00
HTTP 200
2022-05-31 15:36:54 +03:00
[Asserts]
header "Content-Type" contains "text/html"
header "Last-Modified" == "Wed, 21 Oct 2015 07:28:00 GMT"
xpath "//h1" exists # Check we've at least one h1
xpath "normalize-space(//h1)" contains "Welcome"
xpath "//h2" count == 13
xpath "string(//article/@data-id)" startsWith "electric"
```
### Status assert
Check the received HTTP response status code. Status assert consists of the keyword `status` followed by a predicate
function and value.
```hurl
GET https://example.org
2022-12-19 23:30:08 +03:00
HTTP *
2022-05-31 15:36:54 +03:00
[Asserts]
status < 300
```
### Header assert
2022-12-19 23:30:08 +03:00
Check the value of a received HTTP response header. Header assert consists of the keyword `header` followed by the value
of the header, a predicate function and a predicate value.
2022-05-31 15:36:54 +03:00
```hurl
GET https://example.org
2022-12-19 23:30:08 +03:00
HTTP 302
2022-05-31 15:36:54 +03:00
[Asserts]
header "Location" contains "www.example.net"
2022-12-19 23:30:08 +03:00
header "Last-Modified" matches /\d{2} [a-z-A-Z]{3} \d{4}/
2022-05-31 15:36:54 +03:00
```
2022-10-11 10:16:29 +03:00
If there are multiple headers with the same name, the header assert returns a collection, so `count` , `includes` can be
used in this case to test the header list.
Let's say we have this request and response:
```
> GET /hello HTTP/1.1
> Host: example.org
> Accept: */*
2022-12-19 23:30:08 +03:00
> User-Agent: hurl/2.0.0-SNAPSHOT
2022-10-11 10:16:29 +03:00
>
* Response: (received 12 bytes in 11 ms)
*
< HTTP / 1 . 0 200 OK
< Vary: Content-Type
< Vary: User-Agent
< Content-Type: text / html ; charset = utf-8
< Content-Length: 12
< Server: Flask Server
< Date: Fri , 07 Oct 2022 20:53:35 GMT
```
One can use explicit header asserts:
```hurl
GET https://example.org/hello
2022-12-19 23:30:08 +03:00
HTTP 200
2022-10-11 10:16:29 +03:00
[Asserts]
header "Vary" count == 2
header "Vary" includes "User-Agent"
header "Vary" includes "Content-Type"
```
Or implicit header asserts:
```hurl
GET https://example.org/hello
2022-12-19 23:30:08 +03:00
HTTP 200
2022-10-11 10:16:29 +03:00
Vary: User-Agent
Vary: Content-Type
```
2022-10-31 13:50:22 +03:00
### URL assert
2022-10-24 21:58:56 +03:00
2022-10-31 13:50:22 +03:00
Check the last fetched URL. This is most meaningful if you have told Hurl to follow redirection (see [`[Options]`section][options] or
[`--location` option]). URL assert consists of the keyword `url` followed by a predicate function and value.
2022-10-24 21:58:56 +03:00
```hurl
GET https://example.org/redirecting
[Options]
location: true
2022-12-19 23:30:08 +03:00
HTTP 200
2022-10-24 21:58:56 +03:00
[Asserts]
url == "https://example.org/redirected"
```
2022-10-11 10:16:29 +03:00
2022-05-31 15:36:54 +03:00
### Cookie assert
Check value or attributes of a [`Set-Cookie`] response header. Cookie assert
consists of the keyword `cookie` , followed by the cookie name (and optionally a
cookie attribute), a predicate function and value.
Cookie attributes value can be checked by using the following format:
`<cookie-name>[cookie-attribute]` . The following attributes are supported: `Value` ,
`Expires` , `Max-Age` , `Domain` , `Path` , `Secure` , `HttpOnly` and `SameSite` .
```hurl
GET http://localhost:8000/cookies/set
2022-12-19 23:30:08 +03:00
HTTP 200
2022-05-31 15:36:54 +03:00
# Explicit check of Set-Cookie header value. If the attributes are
2022-10-01 11:13:50 +03:00
# not in this exact order, this assert will fail.
2022-05-31 15:36:54 +03:00
Set-Cookie: LSID=DQAAAKEaem_vYg; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly; Path=/accounts; SameSite=Lax;
2023-05-02 09:43:38 +03:00
Set-Cookie: HSID=AYQEVnDKrdst; Domain=localhost; Expires=Wed, 13 Jan 2021 22:23:01 GMT; HttpOnly; Path=/
Set-Cookie: SSID=Ap4PGTEq; Domain=localhost; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly; Path=/
2022-05-31 15:36:54 +03:00
# Using cookie assert, one can check cookie value and various attributes.
[Asserts]
cookie "LSID" == "DQAAAKEaem_vYg"
cookie "LSID[Value]" == "DQAAAKEaem_vYg"
cookie "LSID[Expires]" exists
cookie "LSID[Expires]" contains "Wed, 13 Jan 2021"
cookie "LSID[Max-Age]" not exists
cookie "LSID[Domain]" not exists
cookie "LSID[Path]" == "/accounts"
cookie "LSID[Secure]" exists
cookie "LSID[HttpOnly]" exists
cookie "LSID[SameSite]" equals "Lax"
```
> `Secure` and `HttpOnly` attributes can only be tested with `exists` or `not exists` predicates
2022-09-28 11:24:24 +03:00
> to reflect the [Set-Cookie header] semantics (in other words, queries `<cookie-name>[HttpOnly]`
2022-05-31 15:36:54 +03:00
> and `<cookie-name>[Secure]` don't return boolean).
### Body assert
Check the value of the received HTTP response body when decoded as a string.
Body assert consists of the keyword `body` followed by a predicate function and
value. The encoding used to decode the body is based on the `charset` value in the
`Content-Type` header response.
```hurl
GET https://example.org
2022-12-19 23:30:08 +03:00
HTTP 200
2022-05-31 15:36:54 +03:00
[Asserts]
body contains "< h1 > Welcome!< / h1 > "
```
2023-05-26 14:45:29 +03:00
```hurl
# Our HTML response is encoded with GB 2312 (see https://en.wikipedia.org/wiki/GB_2312)
GET https://example.org/cn
HTTP 200
header "Content-Type" == "text/html; charset=gb2312"
bytes contains hex,c4e3bac3cac0bde7; # 你好世界 encoded in GB 2312
body contains == "你好世界"
```
2022-05-31 15:36:54 +03:00
### Bytes assert
Check the value of the received HTTP response body as a bytestream. Body assert
consists of the keyword `bytes` followed by a predicate function and value.
```hurl
GET https://example.org/data.bin
2022-12-19 23:30:08 +03:00
HTTP 200
2022-05-31 15:36:54 +03:00
[Asserts]
bytes startsWith hex,efbbbf;
2022-12-19 23:30:08 +03:00
bytes count == 12424
header "Content-Length" == "12424"
2022-05-31 15:36:54 +03:00
```
### XPath assert
2023-05-26 14:45:29 +03:00
Check the value of a [XPath] query on the received HTTP body decoded as a string (using the `charset` value in the
`Content-Type` header response). Currently, only XPath 1.0 expression can be used. Body assert consists of the
2022-05-31 15:36:54 +03:00
keyword `xpath` followed by a predicate function and value. Values can be string,
boolean or number depending on the XPath query.
Let's say we want to check this HTML response:
```plain
$ curl -v https://example.org
< HTTP / 1 . 1 200 OK
< Content-Type: text / html ; charset = UTF-8
...
<!doctype html>
< html >
< head >
< title > Example Domain< / title >
...
< / head >
< body >
< div >
< h1 > Example< / h1 >
< p > This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.< / p >
< p > < a href = "https://www.iana.org/domains/example" > More information...< / a > < / p >
< / div >
< / body >
< / html >
```
With Hurl, we can write multiple XPath asserts describing the DOM content:
```hurl
GET https://example.org
2022-12-19 23:30:08 +03:00
HTTP 200
2022-05-31 15:36:54 +03:00
Content-Type: text/html; charset=UTF-8
[Asserts]
xpath "string(/html/head/title)" contains "Example" # Check title
xpath "count(//p)" == 2 # Check the number of < p >
xpath "//p" count == 2 # Similar assert for < p >
xpath "boolean(count(//h2))" == false # Check there is no < h2 >
xpath "//h2" not exists # Similar assert for < h2 >
```
2022-08-19 19:02:15 +03:00
XML Namespaces are also supported. Let's say you want to check this XML response:
```xml
<?xml version="1.0"?>
<!-- both namespace prefixes are available throughout -->
< bk:book xmlns:bk = 'urn:loc.gov:books'
xmlns:isbn='urn:ISBN:0-395-36341-6'>
< bk:title > Cheaper by the Dozen< / bk:title >
< isbn:number > 1568491379< / isbn:number >
< / bk:book >
```
This XML response can be tested with the following Hurl file:
```hurl
GET http://localhost:8000/assert-xpath
2022-12-19 23:30:08 +03:00
HTTP 200
2022-08-19 19:02:15 +03:00
[Asserts]
xpath "string(//bk:book/bk:title)" == "Cheaper by the Dozen"
xpath "string(//*[name()='bk:book']/*[name()='bk:title'])" == "Cheaper by the Dozen"
xpath "string(//*[local-name()='book']/*[local-name()='title'])" == "Cheaper by the Dozen"
xpath "string(//bk:book/isbn:number)" == "1568491379"
xpath "string(//*[name()='bk:book']/*[name()='isbn:number'])" == "1568491379"
xpath "string(//*[local-name()='book']/*[local-name()='number'])" == "1568491379"
```
The XPath expressions `string(//bk:book/bk:title)` and `string(//bk:book/isbn:number)` are written with `bk` and `isbn`
namespaces.
> For convenience, the first default namespace can be used with `_`
2022-05-31 15:36:54 +03:00
### JSONPath assert
Check the value of a [JSONPath] query on the received HTTP body decoded as a JSON
2022-12-19 23:30:08 +03:00
document. JSONPath assert consists of the keyword `jsonpath` followed by a predicate
2022-05-31 15:36:54 +03:00
function and value.
Let's say we want to check this JSON response:
```plain
curl -v http://httpbin.org/json
< HTTP / 1 . 1 200 OK
< Content-Type: application / json
...
{
"slideshow": {
"author": "Yours Truly",
"date": "date of publication",
"slides": [
{
"title": "Wake up to WonderWidgets!",
"type": "all"
},
...
],
"title": "Sample Slide Show"
}
}
```
With Hurl, we can write multiple JSONPath asserts describing the DOM content:
```hurl
GET http://httpbin.org/json
2022-12-19 23:30:08 +03:00
HTTP 200
2022-05-31 15:36:54 +03:00
[Asserts]
jsonpath "$.slideshow.author" == "Yours Truly"
jsonpath "$.slideshow.slides[0].title" contains "Wonder"
jsonpath "$.slideshow.slides" count == 2
jsonpath "$.slideshow.date" != null
jsonpath "$.slideshow.slides[*].title" includes "Mind Blowing!"
```
> Explain that the value selected by the JSONPath is coerced to a string when only
> one node is selected.
In `matches` predicates, metacharacters beginning with a backslash (like `\d` , `\s` ) must be escaped.
Alternatively, `matches` predicate support [Javascript-like Regular expression syntax] to enhance
the readability:
```hurl
GET https://sample.org/hello
2022-12-19 23:30:08 +03:00
HTTP 200
2022-05-31 15:36:54 +03:00
[Asserts]
# Predicate value with matches predicate:
jsonpath "$.date" matches "^\\d{4}-\\d{2}-\\d{2}$"
jsonpath "$.name" matches "Hello [a-zA-Z]+!"
# Equivalent syntax:
jsonpath "$.date" matches /^\d{4}-\d{2}-\d{2}$/
jsonpath "$.name" matches /Hello [a-zA-Z]+!/
```
### Regex assert
Check that the HTTP received body, decoded as text, matches a regex pattern.
```hurl
GET https://sample.org/hello
2022-12-19 23:30:08 +03:00
HTTP 200
2022-05-31 15:36:54 +03:00
[Asserts]
2022-12-19 23:30:08 +03:00
regex "^(\\d{4}-\\d{2}-\\d{2})$" == "2018-12-31"
# Same assert as previous using regex literals
regex /^(\d{4}-\d{2}-\d{2})$/ == "2018-12-31"
2022-05-31 15:36:54 +03:00
```
2022-12-19 23:30:08 +03:00
The regex pattern must have at least one capture group, otherwise the
assert will fail. The assertion is done on the captured group value. When the regex pattern is a double-quoted string,
metacharacters beginning with a backslash in the pattern (like `\d` , `\s` ) must be escaped; literal pattern enclosed by
`/` can also be used to avoid metacharacters escaping.
2022-05-31 15:36:54 +03:00
### SHA-256 assert
Check response body [SHA-256] hash.
```hurl
GET https://example.org/data.tar.gz
2022-12-19 23:30:08 +03:00
HTTP 200
2022-05-31 15:36:54 +03:00
[Asserts]
sha256 == hex,039058c6f2c0cb492c533b0a4d14ef77cc0f78abccced5287d84a1a2011cfb81;
```
### MD5 assert
Check response body [MD5] hash.
```hurl
GET https://example.org/data.tar.gz
2022-12-19 23:30:08 +03:00
HTTP 200
2022-05-31 15:36:54 +03:00
[Asserts]
md5 == hex,ed076287532e86365e841e92bfc50d8c;
```
### Variable assert
```hurl
# Test that the XML endpoint return 200 pets
GET https://example.org/api/pets
2022-12-19 23:30:08 +03:00
HTTP 200
2022-05-31 15:36:54 +03:00
[Captures]
pets: xpath "//pets"
[Asserts]
variable "pets" count == 200
```
### Duration assert
Check the total duration (sending plus receiving time) of the HTTP transaction.
```hurl
GET https://sample.org/helloworld
2022-12-19 23:30:08 +03:00
HTTP 200
2022-05-31 15:36:54 +03:00
[Asserts]
duration < 1000 # Check that response time is less than one second
```
2023-05-03 12:52:31 +03:00
### SSL certificate assert
Check the SSL certificate properties. Certificate assert consists of the keyword `certificate` , followed by the certificate attribute value.
The following attributes are supported: `Subject` , `Issuer` , `Start-Date` , `Expire-Date` and `Serial-Number` .
```hurl
GET https://example.org
HTTP 200
[Asserts]
certificate "Subject" == "CN=example.org"
certificate "Issuer" == "C=US, O=Let's Encrypt, CN=R3"
certificate "Expire-Date" daysAfterNow > 15
certificate "Serial-Number" matches "[0-9af]+"
```
2022-05-31 15:36:54 +03:00
## Body
Optional assertion on the received HTTP response body. Body section can be seen
as syntactic sugar over [body asserts] (with `equals` predicate function). If the
body of the response is a [JSON] string or a [XML] string, the body assertion can
be directly inserted without any modification. For a text based body that is not JSON nor XML,
one can use multiline string that starts with < code > ` ` ` < / code > and ends
with < code > ` ` ` < / code > . For a precise byte control of the response body,
a [Base64] encoded string or an input file can be used to describe exactly
the body byte content to check.
### JSON body
```hurl
# Get a doggy thing:
GET https://example.org/api/dogs/{{dog-id}}
2022-12-19 23:30:08 +03:00
HTTP 200
2022-05-31 15:36:54 +03:00
{
"id": 0,
"name": "Frieda",
"picture": "images/scottish-terrier.jpeg",
"age": 3,
"breed": "Scottish Terrier",
"location": "Lisco, Alabama"
}
```
2022-12-19 23:30:08 +03:00
JSON response body can be seen as syntactic sugar of [multiline string body] with `json` identifier:
~~~hurl
# Get a doggy thing:
GET https://example.org/api/dogs/{{dog-id}}
HTTP 200
```json
{
"id": 0,
"name": "Frieda",
"picture": "images/scottish-terrier.jpeg",
"age": 3,
"breed": "Scottish Terrier",
"location": "Lisco, Alabama"
}
```
~~~
2022-05-31 15:36:54 +03:00
### XML body
~~~hurl
GET https://example.org/api/catalog
2022-12-19 23:30:08 +03:00
HTTP 200
2022-05-31 15:36:54 +03:00
<?xml version="1.0" encoding="UTF-8"?>
< catalog >
< book id = "bk101" >
< author > Gambardella, Matthew< / author >
< title > XML Developer's Guide< / title >
< genre > Computer< / genre >
< price > 44.95< / price >
< publish_date > 2000-10-01< / publish_date >
< description > An in-depth look at creating applications with XML.< / description >
< / book >
< / catalog >
~~~
2022-12-19 23:30:08 +03:00
XML response body can be seen as syntactic sugar of [multiline string body] with `xml` identifier:
~~~hurl
GET https://example.org/api/catalog
HTTP 200
```xml
<?xml version="1.0" encoding="UTF-8"?>
< catalog >
< book id = "bk101" >
< author > Gambardella, Matthew< / author >
< title > XML Developer's Guide< / title >
< genre > Computer< / genre >
< price > 44.95< / price >
< publish_date > 2000-10-01< / publish_date >
< description > An in-depth look at creating applications with XML.< / description >
< / book >
< / catalog >
```
~~~
2022-11-05 20:43:29 +03:00
### Multiline string body
2022-05-31 15:36:54 +03:00
~~~hurl
GET https://example.org/models
2022-12-19 23:30:08 +03:00
HTTP 200
2022-05-31 15:36:54 +03:00
```
Year,Make,Model,Description,Price
1997,Ford,E350,"ac, abs, moon",3000.00
1999,Chevy,"Venture ""Extended Edition""","",4900.00
1999,Chevy,"Venture ""Extended Edition, Very Large""",,5000.00
1996,Jeep,Grand Cherokee,"MUST SELL! air, moon roof, loaded",4799.00
```
~~~
2022-11-05 20:43:29 +03:00
The standard usage of a multiline string is :
2022-05-31 15:36:54 +03:00
~~~
```
line1
line2
line3
```
~~~
2022-12-19 23:30:08 +03:00
#### Oneline string body
2022-05-31 15:36:54 +03:00
2022-12-19 23:30:08 +03:00
For text based response body that do not contain newlines, one can use oneline string, started and ending with < code > ` < / code > .
2022-05-31 15:36:54 +03:00
2022-12-19 23:30:08 +03:00
~~~hurl
POST https://example.org/helloworld
2022-05-31 15:36:54 +03:00
2022-12-19 23:30:08 +03:00
HTTP 200
`Hello world!`
2022-05-31 15:36:54 +03:00
~~~
### Base64 body
2022-12-19 23:30:08 +03:00
Base64 response body assert starts with `base64,` and end with `;` . MIME's Base64 encoding
2022-05-31 15:36:54 +03:00
is supported (newlines and white spaces may be present anywhere but are to be
ignored on decoding), and `=` padding characters might be added.
```hurl
GET https://example.org
2022-12-19 23:30:08 +03:00
HTTP 200
2022-05-31 15:36:54 +03:00
base64,TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIG
FkaXBpc2NpbmcgZWxpdC4gSW4gbWFsZXN1YWRhLCBuaXNsIHZlbCBkaWN0dW0g
aGVuZHJlcml0LCBlc3QganVzdG8gYmliZW5kdW0gbWV0dXMsIG5lYyBydXRydW
0gdG9ydG9yIG1hc3NhIGlkIG1ldHVzLiA=;
```
### File body
To use the binary content of a local file as the body response assert, file body
can be used. File body starts with `file,` and ends with `;` `
```hurl
GET https://example.org
2022-12-19 23:30:08 +03:00
HTTP 200
2022-05-31 15:36:54 +03:00
file,data.bin;
```
File are relative to the input Hurl file, and cannot contain implicit parent
directory (`..`). You can use [`--file-root` option] to specify the root directory
of all file nodes.
2022-10-24 21:58:56 +03:00
2022-05-31 15:36:54 +03:00
[predicates]: #predicates
[header assert]: #header -assert
[captures]: /docs/capturing-response.md#query
[data attributes]: https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes
[`Set-Cookie`]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
[Set-Cookie header]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
[XPath]: https://en.wikipedia.org/wiki/XPath
[JSONPath]: https://goessner.net/articles/JsonPath/
[body asserts]: #body -assert
[JSON]: https://www.json.org
[XML]: https://en.wikipedia.org/wiki/XML
[Base64]: https://en.wikipedia.org/wiki/Base64
2022-09-02 15:45:54 +03:00
[`--file-root` option]: /docs/manual.md#file-root
2022-05-31 15:36:54 +03:00
[Javascript-like Regular expression syntax]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
[MD5]: https://en.wikipedia.org/wiki/MD5
[SHA-256]: https://en.wikipedia.org/wiki/SHA-2
2022-10-24 21:58:56 +03:00
[options]: /docs/request.md#options
[`--location` option]: /docs/manual.md#location
2022-12-19 23:30:08 +03:00
[multiline string body]: #multiline -string-body
[filters]: /docs/filters.md
2023-03-07 13:37:31 +03:00
[count]: /docs/filters.md#count