Python Functional HTTP Testing With Gabbi
Tue June 06 2017

Continuing with my journey in contributing to Gnocchi, I have learned of an excellent tool which Gnocchi uses to test its API from a HTTP request approach. The tool is called Gabbi.

Coming from a Ruby, Rails, and Rspec background, I was very pleased on learning how Gabbi works and how to use it. Gabbi uses YAML to construct the tests that will hit the API endpoints, in a similar way to using Rspec's DSL when making request specs.

For my latest contribution to Gnocchi, I was working on an issue where newly created metrics were not being returned in the response from /v1/resource/generic/:id/metric. After applying the fixes and submitting the patch, it was also necessary to update some of the functional Gabbi tests that belong to this specific use case.

One of these original tests is shown below:

gnocchi/tests/functional/gabbits/resource.yaml:

    - name: post metric at generic
      POST: /v1/resource/generic/85C44741-CC60-4033-804E-2D3098C7D2E9/metric
      request_headers:
          x-user-id: 0fbb231484614b1a80131fc22f6afc9c
          x-project-id: f3d41b770cc14f0bb94a1d5be9c0e3ea
          content-type: application/json
      status: 204
      data:
          electron.spin:
              archive_policy_name: medium
      response_headers:

We can see how the request is nicely defined using YAML, specifying required stuff such as the action, request headers, and data.

For the response, we are specifying that it should return a 204 HTTP status code. For my patch I actually had to change the status code to 200.

Additionally, during the code review someone suggested to also test for the response JSON contents of the returned metrics. This is when I learned about the response_json_paths expectation, which allows us to access the response's JSON body and assert their content.

For my patch, I simply used this expectation to test against the metric's name and resource id, making the test now look like this:

    - name: post metric at generic
      POST: /v1/resource/generic/85C44741-CC60-4033-804E-2D3098C7D2E9/metric
      request_headers:
          x-user-id: 0fbb231484614b1a80131fc22f6afc9c
          x-project-id: f3d41b770cc14f0bb94a1d5be9c0e3ea
          content-type: application/json
      status: 200
      data:
          electron.spin:
              archive_policy_name: medium
      response_headers:
      response_json_paths:
          $[/name][1].name:: electron.spin
          $[/name][1].resource_id: 85C44741-CC60-4033-804E-2D3098C7D2E9

Note how we are using $ and an index 1 to access the response's first item in a similar fashion to accessing an array. The response actually return's a JSON array of metrics. Another important thing is that we are sorting the returned metrics by name using [/name], as documented in Gabbi. The reason we do this is because the order of the response's items in Gnocchi's Gabbi tests cannot be predicted.

References


python gabbi gnocchi tdd bdd web-dev backend yaml

Comments

comments powered by Disqus