January 22, 2020 David Hallinan Guides Getting Started with API Testing Using Pytest APIs are the backbone of the modern web. The flow of data throughout the internet has taken many forms throughout its evolution, and as a result, has gone through many different iterations of architectural setup. Most commonly, a RESTful architecture is implemented to act as the gatekeeper between a relational database and the requests that come in for its data. Parsing those requests and sending out the relevant data is essentially the primary role of the RESTful API. When testing an API, more is better. Test-Driven Development, or TDD, is the process of writing tests before you write the actual functionality in the aim of adhering your code to the confines of the test it is created to pass. For example, if you were to test a person object in your database, you might write a test to ensure that all new person objects must be instantiated with a full name. This functionality should also be set as required on your data model, but by writing a test to check for its existence as well, you are able to create a much more comprehensive and thorough way of securing your database from harmful entries and relaying helpful messages back to your users. So, let’s say you’ve built out an API with Django or Flask, using one of the most popular languages of the day: Python. It’s now time to test your API and ensure that not only is the correct data coming back from each of your routes but that the proper response codes and error messages are being relayed when necessary. There are many different frameworks for this type of testing available, but the most popular is still pytest. In this piece, we’ll go over how to get started with testing your API through this powerful framework. PyTest The pytest framework makes it easy to write small tests, but it scales to support complex functional testing for applications and libraries as well. Let’s look at an example of a simple test called test_sample.py: def inc(x): return x + 1 def test_answer(): assert inc(3) == 5 123456 def inc(x): return x + 1 def test_answer(): assert inc(3) == 5 Our inc function simply takes a number and adds 1 to it. Our test_answer function is where pytest looks to return a passing or failing message, though. The usage of the word assert will look for the truthiness in the statement that follows it. In this case, it is expecting the output of inc(3) to equal 5. Since we know that 3 + 1 == 4, this case will return failing message. If your tests are placed in the correct location according to the documentation of pytest, executing your test cases is as simple as just writing pytest in the terminal when you have navigated to the root of your application. The response from running your tests will come back like so: =========================== test session starts ============================ platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 1 item test_sample.py F [100%] ================================= FAILURES ================================= _______________________________ test_answer ________________________________ def test_answer(): > assert inc(3) == 5 E assert 4 == 5 E + where 4 = inc(3) test_sample.py:6: AssertionError ============================ 1 failed in 0.12s ============================= 123456789101112131415161718 =========================== test session starts ============================platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.ycachedir: $PYTHON_PREFIX/.pytest_cacherootdir: $REGENDOC_TMPDIRcollected 1 item test_sample.py F [100%] ================================= FAILURES =================================_______________________________ test_answer ________________________________ def test_answer():> assert inc(3) == 5E assert 4 == 5E + where 4 = inc(3) test_sample.py:6: AssertionError============================ 1 failed in 0.12s ============================= In the example, the assert statement checks whether the return value of your inc() function is what you have told it to expect. In this case, it does not, so your test fails. In order to pass this simple test, you can rewrite the test to produce a passing grade. def inc(x): return x + 1 def test_answer(): assert inc(3) == 4 123456 def inc(x): return x + 1 def test_answer(): assert inc(3) == 4 Since we know the simple math above checks out, this test will pass and return a successful message from pytest. Obviously, this is as simple as tests go, but the assert statement can be used to monitor the inner-workings of any route in your API. Pytest supports a large number of features and functionality to aid in your API testing needs: It supports running the same set of test cases or test suites with multiple different parameters.Test cases can be skipped over based on conditions or prerequisites.Assertion and error analysis and reporting are quite descriptive compared to similar testing frameworks.Testing both simple and complex functionality is quite similar in the structure of the test case.Auto-discovery of tests means that as long as the tests are within your folder structure in your application, they will be run when invoked through a global command. There are other frameworks for testing Python, such as Robot Framework. We covered the introduction to Pytest in this article because of its incredibly easy implementation for beginners and scalability for complex testing needs. For your application, go ahead and research as many of these frameworks as you can, but generally, pytest will probably be able to accommodate your application’s needs. If you are building an application and need helpful tips and strategies for putting together your API, head on over and check out the recently released Kloudless Official Guide to API Integrations, available now!