# Example Tests

What follows is a commented example of some tests in a single file demonstrating many of the Test Format features. See Loading and Running Tests for the Python needed to integrate with a testing harness.

    # Fixtures can be used to set any necessary configuration, such as a
    # persistence layer, and establish sample data. They operate per
    # file. They are context managers, each one wrapping the next in the
    # sequence.

    fixtures:
        - ConfigFixture
        - SampleDataFixture

    # There is an included fixture named "SkipAllFixture" which can be
    # used to declare that all the tests in the given file are to be
    # skipped.

    # Each test file can specify a set of defaults that will be used for
    # every request. This is useful for always specifying a particular
    # header or always requiring SSL. These values will be used on every
    # test in the file unless overriden. Lists and dicts are merged one
    # level deep, except for "data" which is copied verbatim whether it
    # is a string, list or dict (it can be all three).

    defaults:
        ssl: True
        request_headers:
            x-my-token: zoom

    # The tests themselves are a list under a "tests" key. It's useful
    # to use plenty of whitespace to help readability.

    tests:

    # Each request *must* have a name which is unique to the file. When it
    # becomes a TestCase the name will be lowercased and spaces will
    # become "_". Use that generated name when limiting test runs.

        - name: a test for root
          desc: Some explanatory text that could be used by other tooling

    # The URL can either be relative to a host specified elsewhere or
    # be a fully qualified "http" or "https" URL. *You* are responsible
    # for url-encoding the URL.

          url: /
          method: GET

    # If no status or method are provided they default to "200" and
    # "GET".

    # Instead of explicitly stating "url" and "method" you can join
    # those two keys into one key representing the method. The method
    # *must* be uppercase.

        - name: another test for root
          desc: Same test as above but with GET key
          GET: /

    # A single test can override settings in defaults (set above).

        - name: root without ssl redirects
          ssl: False
          GET: /
          status: 302

    # When evaluating response headers it is possible to use a regular
    # expression to not have to test the whole value. Regular expressions match
    # anywhere in the output, not just at the beginning.

          response_headers:
              location: /^https/

    # By default redirects will not be followed. This can be changed.

        - name: follow root without ssl redirect
          ssl: False
          redirects: True
          GET: /
          status: 200 # This is the response code after the redirect.

    # URLs can express query parameters in two ways: either in the url
    # value directly, or as query_parameters. If both are used then
    # query_parameters are appended. In this example the resulting URL
    # will be equivalient to
    # /foo?section=news&article=1&article=2&date=yesterday
    # but not necessarily in that order.

        - name: create a url with parameters
          GET: /foo?section=news
          query_parameters:
              article:
                  - 1
                  - 2
              date: yesterday

    # Request headers can be used to declare media-type choices and
    # experiment with authorization handling (amongst other things).
    # Response headers allow evaluating headers in the response. These
    # two together form the core value of gabbi.

        - name: test accept
          GET: /resource
          request_headers:
              accept: application/json
          response_headers:
              content-type: /application/json/

    # If a header must not be present in a response at all that can be
    # expressed in a test as follows.

        - name: test forbidden headers
          GET: /resource
          response_forbidden_headers:
              - x-special-header

    # All of the above requests have defaulted to a "GET" method. When
    # using "POST", "PUT" or "PATCH", the "data" key provides the
    # request body.

        - name: post some text
          POST: /text_repo
          request_headers:
              content-type: text/plain
          data: "I'm storing this"
          status: 201

    # If the data is not a string, it will be transformed into JSON.
    # You must supply an appropriate content-type request header.

        - name: post some json
          POST: /json_repo
          request_headers:
              content-type: application/json
          data:
              name: smith
              abode: castle
          status: 201

    # If the data is a string prepended with "<@" the value will be
    # treated as the name of a file in the same directory as the YAML
    # file. Again, you must supply an appropriate content-type. If the
    # content-type is one of several "text-like" types, the content will
    # be assumed to be UTF-8 encoded.

        - name: post an image
          POST: /image_repo
          request_headers:
              content-type: image/png
          data: <@kittens.png

    # A single request can be marked to be skipped.

        - name: patch an image
          skip: patching images not yet implemented
          PATCH: /image_repo/12d96fb8-e78c-11e4-8c03-685b35afa334

    # Or a single request can be marked that it is expected to fail.

        - name: check allow headers
          desc: the framework doesn't do allow yet
          xfail: True
          PUT: /post_only_url
          status: 405
          response_headers:
              allow: POST

    # The body of a response can be evaluated with response handlers.
    # The most simple checks for a set of strings anywhere in the
    # response. Note that the strings are members of a list.

        - name: check for css file
          GET: /blog/posts/12
          response_strings:
              - normalize.css

    # For JSON responses, JSONPath rules can be used.

        - name: post some json get back json
          POST: /json_repo
          request_headers:
              content-type: application/json
          data:
              name: smith
              abode: castle
          status: 201
          response_json_paths:
              $.name: smith
              $.abode: castle

    # Requests run in sequence. One test can make reference to the test
    # immediately prior using some special variables.
    # "$LOCATION" contains the "location" header in the previous
    # response.
    # "$HEADERS" is a pseudo dictionary containing all the headers of
    # the previous response.
    # "$ENVIRON" is a pseudo dictionary providing access to the current
    # environment.
    # "$RESPONSE" provides access to the JSON in the prior response, via
    # JSONPath. See https://github.com/h2non/jsonpath-ng for
    # jsonpath formatting.
    # $SCHEME and $NETLOC provide access to the current protocol and
    # location (host and port).

        - name: get the thing we just posted
          GET: $LOCATION
          request_headers:
              x-magic-exchange: $HEADERS['x-magic-exchange']
              x-token: $ENVIRON['OS_TOKEN']
          response_json_paths:
              $.name: $RESPONSE['$.name']
              $.abode: $RESPONSE['$.abode']
          response_headers:
              content-location: /$SCHEME://$NETLOC/

    # For APIs where resource creation is asynchronous it can be
    # necessary to poll for the resulting resource. First we create the
    # resource in one test. The next test uses the "poll" key to loop
    # with a delay for a set number of times.

        - name: create asynch
          POST: /async_creator
          request_headers:
              content-type: application/json
          data:
              name: jones
              abode: bungalow
          status: 202

        - name: poll for created resource
          GET: $LOCATION
          poll:
              count: 10 # try up to ten times
              delay: .5 # wait .5 seconds between each try
          response_json_paths:
              $.name: $RESPONSE['$.name']
              $.abode: $RESPONSE['$.abode']

    # Test HTTP calls using different HTTP protocol versions

        - name: HTTP/1.1 test
          GET: /
          response_headers:
            http_protocol_version: HTTP/1.1

        - name: HTTP/2 test
          GET: /
          http_version: 2
          response_headers:
            http_protocol_version: HTTP/2