FLASK TDD
FLASK TDD

The series of this tutorial has definitely evolved right from getting started with flask python framework to Building RESTful APIs using flask and now we are going to write few tests to our app that we had uploaded on GitHub

Table of content

  • Introduction to Test Driven Development
  • Understanding unittest
  • Writing tests for myblog app
  • Automating tests using Travis Continuous Integration

Introduction to Test Driven Development TDD

Test driven development TDD is a technique that allows the software developer to write the software test cases or in other words communicate expectations, write the bare minimum code to make the test pass / deliver the expected results that he had communicated and then add more codes to stabilize the app.

Incase the tests fails after writing the code i.e deliver what was not expected, the developer has to work on the code and not the tests until the test pass.

TDD is one way of thinking through software requirements or design before writing the functional code.

There are 4 popular python libraries that are used when writing tests in python, these are:

  1. Unittests*
  2. Pytests
  3. Nose*
  4. Doctest

Please note that apart from unittest, the rest are also referred to as test runners.

Test driven development
Test driven development

Understanding unittest

The unittest module comes pre-installed with Python

The standard workflow is:

  • You define your own class derived from unittest.TestCase.
  • Then you fill it with methods that start with ‘test_’.
  • You run the tests by placing unittest.main() in your file, usually at the bottom.

Test discovery

  • Let’s say that you’ve got a bunch of test files. It would be annoying to have to run each test file separately. That’s where test discovery comes in handy.
  • In our case, all of my test code (one file for now) is in ‘simple_example’.
  • To run all of the unittests in there, use python -m unittest discover simple_example.

For nose to discover your tests you must:

  • Name my test modules/files starting with ‘test_’.
  • Name my test functions starting with ‘test_’.
  • Name my test classes starting with ‘Test’.
  • Name my test methods starting with ‘test_’.
  • Make sure all packages with test code have an ‘init.py’ file.
full video

Enough with the theory! lets get our hands dirty

Writing tests for myblog app

Incase you don’t know what myblog app is, kindly check and read through Building RESTful APIs using Flask .

In our test_myblog.py file, we are going to add a few lines of code.

import json
import unittest
from run import app

class TestMyBlogApp(unnittest.TestCase):
    """docstring for TestMyBlogApp"""

    def setUp(self):
        pass

    def test_posting_a_blog(self):
        pass

    def test_getting_all_blogs(self):
        pass

    def test_getting_a_single_blog(self):
        pass

    def test_editing_a_blog(self):
        pass

    def test_deleting_a_blog(self):
        pass


if __name__ == '__main__':
    unnittest.main()
  • We are importing unittest since its the main testing library that python has by default and that we are going to use inside this app.
  • Since we want to test our blog app, we definitely have to import the name of our flask app.
  • TestMyBlogApp class is the name of our test class and we will then include all our test methods inside this class. We inherit all testcases that are associated with the unittest library as a package into this class
  • The setUp method is used will be used to construct all our test
  • We then add all the bear minimum test method for each endpoint we created.

Update all our tests methods. Ta-da!

    def setUp(self):
        self.app = app
        self.client = self.app.test_client()
        self.data = {
            "tittle": "Religion is all about faith",
            "description": "Some serious and useful content here"
        }
        self.da = {
            "tittle": "UPDATED: Religion is all about faith",
            "description": "UPDATED: Some serious and useful content here"
        }

    def test_posting_a_blog(self):
        resp = self.client.post(path='/blog', data=json.dumps(self.data), content_type='application/json')
        self.assertEqual(resp.status_code, 201)

    def test_getting_all_blogs(self):
        resp = self.client.get(path='/blog', content_type='application/json')
        self.assertEqual(resp.status_code, 200)

    def test_getting_a_single_blog(self):
        post = self.client.post(path='/blog', data=json.dumps(self.data), content_type='application/json')
        int_id = int(post.json['blog_id'])
        path = '/blog/{}'.format(int_id)
        response = self.client.get(path, content_type='application/json')
        self.assertEqual(response.status_code, 200)

    def test_editing_a_blog(self):
        post = self.client.post(path='/blog', data=json.dumps(self.data), content_type='application/json')
        int_id = int(post.json['blog_id'])
        path = '/blog/{}'.format(int_id)
        response = self.client.put(path, data=json.dumps(self.da), content_type='application/json')
        self.assertEqual(response.status_code, 200)

    def test_deleting_a_blog(self):
        post = self.client.post(path='/blog', data=json.dumps(self.data), content_type='application/json')
        int_id = int(post.json['blog_id'])
        path = '/blog/{}'.format(int_id)
        response = self.client.delete(path, content_type='application/json')
        self.assertEqual(response.status_code, 200)

Wow, what did we just do? click here for video

In our setUp method, we first instantiate out app to self.app. We then use the test_client() function which is a unittest function that allows us to run test on a flask app. Its simply meant to run the app and execute all its routes and compare the outputs within the asserts function.

In all the rest of the test methods , we are majorly sending sending request specifying the the request type. for example

resp = self.client.post(path='/blog', data=json.dumps(self.data), content_type='application/json')

In the line above we are calling our route http://127.0.0.1:5000/blog and sending a post request self.client.post() , providing the data and content types. We then listen for the status code and compare it with the status code we are providing. self.assertEqual(result, response)

Running the tests

Before we run the test, please install nosetests

(env) $ pip install nose

Now run the test with and without verbose

Without verbose:

(env) andelas-MacBook-Pro:one Mcogol$ nosetests test_myblog.py 
.....
-----------------------------------------------------------------
Ran 5 tests in 0.026s
OK

With verbose:

(env) andelas-MacBook-Pro:one Mcogol$ nosetests -v test_myblog.py 
test_deleting_a_blog (test_myblog.TestMyBlogApp) ... ok
test_editing_a_blog (test_myblog.TestMyBlogApp) ... ok
test_getting_a_single_blog (test_myblog.TestMyBlogApp) ... ok
test_getting_all_blogs (test_myblog.TestMyBlogApp) ... ok
test_posting_a_blog (test_myblog.TestMyBlogApp) ... ok

----------------------------------------------------------------
Ran 5 tests in 0.024s
OK

In the next tutorial, we are going to automate testing and add Travis CI.

Checkout the updated code on github and more explanations on YouTube.

Was this tutorial helpful? Let us know in the comment section by hitting the thumbs up. Like out youtube channel and leave a comment.

2 COMMENTS

  1. Hi, I really love this blog and the YouTube channel, thank you for that good heart of sharing what you have with others. This is the truth, I have been searching the entire web and I have found no organized content like this one. God bless you

LEAVE A REPLY

Please enter your comment!
Please enter your name here