Codementor Events

Building a RESTful Blog APIs using python and flask - Part 3

Published Aug 02, 2018
Building a RESTful Blog APIs using python and flask - Part 3

Summary from Part 1 and Part 2

In part 1, we covered how to create a simple RESTful API with four basic CRUD operation including how to carry out user Authentications using JSON Web Token(JWT). We learned about configuring Flask environment, creating models, making and applying migrations to the DB, grouping resources using flask blueprint, validating the authenticity of a user using JWT token. In part 2, we completed our app endpoints by designing 5 blogposts endpoints.

This part will be the concluding part, we'll discuss how to set up unit tests for our app and also how to host it on Heroku.

Test

“Tests are stories we tell the next generation of programmers on a project.”
― Roy Osherove

Testing is a software development process in which an application is tested independently. It is just a way of ensuring our apps work as expected whenever we make any changes to the code.

Todo

  1. Install Tests Dependencies
  2. Write tests using python unittest package
  3. Get test coverage using pytest-cov package

1. Install Tests Dependencies
Install the following packages

 $ pipenv install pytest-cov pytest

You can read more about how those packages work online.

2. Write tests using python unittest package
To make this simple, our tests will only cover user's endpoint.
Before we continue, we need to create a testing configuration in config.py file - to do that, add the following code to config.py

 #####################
 # existing code here#
 #####################
 class Testing(object):
    """
    Development environment configuration
    """
    TESTING = True
    JWT_SECRET_KEY = os.getenv('JWT_SECRET_KEY')
    SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_TEST_URL')
    SQLALCHEMY_TRACK_MODIFICATIONS=False

 app_config = {
  #####################
    # existing code here#
    #####################
    'testing': Testing
 }

Next, create a new folder inside your src folder and call it tests. Create __init__.py and test_user.py file inside the tests folder
Add the following code to test_user.py

alt

Here, we did the following;

  • CreatedUsersTest class that inherited from unittest.TestCase
  • Created setUp method - this will run before each test case method. We use this method to create our app and also create all database tables needed by the app.
  • tearDown method - this will run at the end of each test case method - we use this method to drop the db after each test case method run, since all our tests are supposed to be independent of each other(meaning they should be able to run independently of each other).
  • Finally our test case methods covers some scenarios which includes user creation with valid credentials, user creation with invalid credentials, user login with both valid and invalid credentials, updating user's profiles and deleting user's account. Note that every test method name need to start with test that is how pytest will recognized it as a test method when it's time to run the test.

3. Get test Code coverage using pytest-cov
Code coverage shows the percentage of our code that are cover in the test.
To run the tests and also get its code coverage, use the following command

  $ pytest --cov=src

You should see something similar to
Screen Shot 2018-07-30 at 10.33.34 AM.png

To exclude tests folder from the coverage, let's create a new config file in the project root directory and name it .coveragerc and add the following to the file

 [run]
 omit = src/tests/*

Now run the tests with $ py.test --cov=src --cov-config .coveragerc

Deploy on Heroku

YAY!! Finally, let's deploy the app to HEROKU.
STEP 1
Register an account on Heroku

STEP 2
Create a new project on your Heroku account and name it anything you like - in my case I named it blogapi-tutorial.
After creating a project you should see something similar to this on your Heroku dashboard
Screen Shot 2018-07-30 at 10.52.55 AM.png

STEP 3
Connect the project to your git hub repository by clicking on deploy tab and click on Github connect.
Screen Shot 2018-07-30 at 10.55.34 AM.png
Note: This assumes you have a Github account and you have your code on Github.

STEP 4
Currently, if you set production as your FLASK_ENV you will get a warning telling you not to use flask server for production that is because it is not advisable to use flask development server in production.
Screen Shot 2018-07-30 at 11.01.27 AM.png

For production, we'll use WSGI(Web Server Gateway Interface) server instead. One implementation of WSGI is gunicorn.
Install gunicorn package using $ pipenv gunicorn.

Also we need to make some adjustment to run.py code, update run.py with the following

 # /run.py
 import os
 from src.app import create_app

 env_name = os.getenv('FLASK_ENV')
 app = create_app(env_name)

 if __name__ == '__main__':
   port = os.getenv('PORT')
   # run app
   app.run(host='0.0.0.0', port=port)

Don't forget to set PORT in your system's enviroment.

Next, create Procfile file in your project root and add the following

 web: gunicorn run:app

Stage, Commit and push your code to Github.

STEP 5
On the Heroku, add all the required environment variables by clicking on settings tab on the dashboard
Screen Shot 2018-07-30 at 11.13.10 AM.png

You can create a free PostgreSQL database with the Heroku PostgreSQL add-ons or you can use ElephantSql.

STEP 6
If Step 1 to 5 was successful, then you're ready to deploy your app.
On Heroku click on deploy tab, select the branch you want to deploy and click on "Deploy Branch" button

Screen Shot 2018-07-30 at 11.20.49 AM.png

Boom!!!!!!!!!!! Congratulation your app is live

To open it, click on "Open app" at the top right corner of the project dashboard on Heroku
Screen Shot 2018-07-30 at 11.25.05 AM.png

Screen Shot 2018-07-30 at 11.26.12 AM.png

You can also deploy the app to Heroku using their cli tool, here is Heroku documentation on how to deploy python app

Conclusion

In this concluding part, we learned the following;

  • Write Python Unit Test using unittest package
  • Deploy app to Heroku.

You can check the complete code on github by clicking here.

Checkout Part 1 and Part 2 of this series

Like, Comments and share this post if you learned one or two things from it.

Discover and read more posts from Olawale Aladeusi
get started
post commentsBe the first to share your opinion
Jericho Ruz
3 years ago

Same project, but with some improvements and with paypal service and email notifications https://github.com/jerichoruz/backbloggie

Gautier Prentout
5 years ago

Hi,

I encounter a problem in test_user.py, when i run “pytest --cov=src”

error :

‘’‘from …app import create_app, db
E ValueError: attempted relative import beyond top-level package’’’

It looks like a project structure issue , anyone had this bug ?

Nishant Ranjan
5 years ago

I do have the same issue. Have your resolved?

pavankumar.ag
5 years ago

"pytest --cov=src " giving error at db.create_all()

"self = <src.tests.test_users.UsersTest testMethod=test_delete_user>

def setUp(self):
    """
    Test Setup
    """
    self.app = create_app("testing")
    self.client = self.app.test_client
    self.user = {
        'name': 'olawale',
        'email': 'olawale@mail.com',
        'password': 'passw0rd!'
    }

    with self.app.app_context():
        # create all tables
      db.create_all()

src/tests/test_users.py:26:


…/.virtualenvs/blog_api-cmPGZ29r/lib/python3.7/site-packages/flask_sqlalchemy/init.py:963: in create_all
self._execute_for_all_tables(app, bind, ‘create_all’)
…/.virtualenvs/blog_api-cmPGZ29r/lib/python3.7/site-packages/flask_sqlalchemy/init.py:955: in _execute_for_all_tables
op(bind=self.get_engine(app, bind), **extra)
…/.virtualenvs/blog_api-cmPGZ29r/lib/python3.7/site-packages/flask_sqlalchemy/init.py:896: in get_engine
return connector.get_engine()
…/.virtualenvs/blog_api-cmPGZ29r/lib/python3.7/site-packages/flask_sqlalchemy/init.py:556: in get_engine
self._sa.apply_driver_hacks(self._app, info, options)


self = <SQLAlchemy engine=None>, app = <Flask ‘src.app’>, info = None, options = {‘convert_unicode’: True}

def apply_driver_hacks(self, app, info, options):
    """This method is called before engine creation and used to inject
    driver specific hacks into the options.  The `options` parameter is
    a dictionary of keyword arguments that will then be used to call
    the :func:`sqlalchemy.create_engine` function.

    The default implementation provides some saner defaults for things
    like pool sizes for MySQL and sqlite.  Also it injects the setting of
    `SQLALCHEMY_NATIVE_UNICODE`.
    """
  if info.drivername.startswith('mysql'):

E AttributeError: ‘NoneType’ object has no attribute ‘drivername’

…/.virtualenvs/blog_api-cmPGZ29r/lib/python3.7/site-packages/flask_sqlalchemy/init.py:830: AttributeError"

Gautier Prentout
5 years ago

I had the same error and it was due to a wrong environment variable, DATABASE_URL was not correctly set.

Kudakwashe Siziva
4 years ago

What did you set the DATABASE_URL to? Is is the same db with app or a different one for tests

Show more replies