patternpythonMinor
A systematic way to test RESTful APIs with curl?
Viewed 0 times
apissystematicwithwaytestrestfulcurl
Problem
I have noticed during integration testing that I actually work on recurring use cases with restful APIs (or in general HTTP interfaces) I check here and there with bash+cURL.
It starts looking quite messy and gets harder to maintain. Why deliver mess?
Typical use cases are:
What I have found so far and consider a workable option without reinventing the wheel, is:
Then I could have, for example, one unit test per service I'd like to check:
Does this make sense or is there a more high-level (but not too complex to pick up and integrate) tooling?
It starts looking quite messy and gets harder to maintain. Why deliver mess?
Typical use cases are:
- Check that an URL returns http response code e.g. 200
- Check that content type matches some MIME you need in that case
- Check that returned content matches some pattern, or passes an abstract validation procedure
What I have found so far and consider a workable option without reinventing the wheel, is:
- have a go with PyCurl - in a hope it fully implements all cURL options esp. proxying but also other switches I might need
- use Python's built in unit testng
Then I could have, for example, one unit test per service I'd like to check:
import unittest, pycurl
class TestService (unittest.TestCase):
def test_1(self):
self.assertEqual(pycurl.returncode("some_url"), 200)
def test_2(self):
self.assertTrue(pycurl.response("some_url").matches ("xxx") )
def test_3(self):
self.assertTrue (pycurl.ContentType("some_url").equal("xxx"))
if __name__ == '__main__':
unittest.main()Does this make sense or is there a more high-level (but not too complex to pick up and integrate) tooling?
Solution
You could look at tools such as Postman which focuses on testing REST APIs with JavaScript - it has some nice features but you lose the use of Python.
Instead, I'd suggest looking at REST-related plugins for pytest, a Python test framework that simplifies your test code, while still running tests written using
Pytest has a huge set of plugins that simplify various tasks, including:
-
Tavern, which specialises in testing REST APIs and seems highly relevant here - sort of "Postman for Python unit tests".
-
pytest-curl-report - when testing with the
-
Testinfra - focuses on server testing (e.g. state of OS packages, files, processes, etc, usually tested on remote servers) - strongly recommended if you also need this type e.g. to test Ansible code.
If you don't like Tavern, you can of course use
Pytest lets you write all tests with a plain
Instead, I'd suggest looking at REST-related plugins for pytest, a Python test framework that simplifies your test code, while still running tests written using
unittest. - For example, writing parameterised tests avoids boring and error-prone duplicate test code.
Pytest has a huge set of plugins that simplify various tasks, including:
-
Tavern, which specialises in testing REST APIs and seems highly relevant here - sort of "Postman for Python unit tests".
-
pytest-curl-report - when testing with the
requests library, will print a curl command you can use to reproduce the error from shell. -
Testinfra - focuses on server testing (e.g. state of OS packages, files, processes, etc, usually tested on remote servers) - strongly recommended if you also need this type e.g. to test Ansible code.
- For those who use Puppet or Chef, Testinfra is similar to Beaker (with RSpec), ServerSpec or InSpec.
If you don't like Tavern, you can of course use
pycurl with pytest, which makes it easier to diagnose exactly what failed. This example from the pytest-curl-report site uses only generic pytest features:$ py.test test.py
============================= test session starts ==============================
platform darwin -- Python 2.7.9 -- py-1.4.27 -- pytest-2.6.4
plugins: curl-report, httpbin, cache, capturelog, cov, flakes, pep8
collected 1 items
test.py F
=================================== FAILURES ===================================
______________________________ test_requests_get _______________________________
def test_requests_get():
r = requests.get('http://httpbin.org/get')
> assert False
E assert False
test.py:7: AssertionErrorPytest lets you write all tests with a plain
assert, and optionally include a helpful message as part of the output. For example, one of your tests could be written:def test_2():
assert pycurl.response("some_url").matches ("xxx"), "xxx not found in response"Code Snippets
$ py.test test.py
============================= test session starts ==============================
platform darwin -- Python 2.7.9 -- py-1.4.27 -- pytest-2.6.4
plugins: curl-report, httpbin, cache, capturelog, cov, flakes, pep8
collected 1 items
test.py F
=================================== FAILURES ===================================
______________________________ test_requests_get _______________________________
def test_requests_get():
r = requests.get('http://httpbin.org/get')
> assert False
E assert False
test.py:7: AssertionErrordef test_2():
assert pycurl.response("some_url").matches ("xxx"), "xxx not found in response"Context
StackExchange DevOps Q#2569, answer score: 9
Revisions (0)
No revisions yet.