Configuring your service
We can configure the service to use environment variables to change the behavior. For containers, this is a fantastic alternative to using configuration files, as it allows immutable containers that get their configuration injected. This is in line with the Twelve-Factor App (https://12factor.net/config) principles and allows for good separation between code and configuration, and the setting up of the different deploys that the code might be used for.
This allows the configuration of the database to connect to, so we can choose between the SQLite backend or PostgreSQL.
We will configure the tests to access our newly created database container. To do that, we first need the ability to choose between either SQLite or PostgreSQL through configuration. Check out the ./ThoughtsBackend/thoughts_backend/db.py file:
import os
from pathlib import Path
from flask_sqlalchemy import SQLAlchemy
DATABASE_ENGINE = os.environ.get('DATABASE_ENGINE', 'SQLITE')
if DATABASE_ENGINE == 'SQLITE':
dir_path = Path(os.path.dirname(os.path.realpath(__file__)))
path = dir_path / '..'
# Database initialisation
FILE_PATH = f'{path}/db.sqlite3'
DB_URI = 'sqlite+pysqlite:///{file_path}'
db_config = {
'SQLALCHEMY_DATABASE_URI': DB_URI.format(file_path=FILE_PATH),
'SQLALCHEMY_TRACK_MODIFICATIONS': False,
}
elif DATABASE_ENGINE == 'POSTGRESQL':
db_params = {
'host': os.environ['POSTGRES_HOST'],
'database': os.environ['POSTGRES_DB'],
'user': os.environ['POSTGRES_USER'],
'pwd': os.environ['POSTGRES_PASSWORD'],
'port': os.environ['POSTGRES_PORT'],
}
DB_URI = 'postgresql://{user}:{pwd}@{host}:{port}/{database}'
db_config = {
'SQLALCHEMY_DATABASE_URI': DB_URI.format(**db_params),
'SQLALCHEMY_TRACK_MODIFICATIONS': False,
}
else:
raise Exception('Incorrect DATABASE_ENGINE')
db = SQLAlchemy()
When using the DATABASE_ENGINE environment variable set to POSTGRESQL, it will configure it properly. Other environment variables will need to be correct; that is, if the database engine is set to PostgreSQL, the POSTGRES_HOST variable needs to be set up.
Environment variables can be stored individually in the docker-compose.yaml file, but it's more convenient to store multiple ones in a file. Let's take a look at environment.env:
DATABASE_ENGINE=POSTGRESQL
POSTGRES_DB=thoughts
POSTGRES_USER=postgres
POSTGRES_PASSWORD=somepassword
POSTGRES_PORT=5432
POSTGRES_HOST=db
Note that the definition of users, and so on is in line with the arguments to create Dockerfile for testing. POSTGRES_HOST is defined as db, which is the name of the service.
Our testing service using the PostgreSQL container then gets defined as follows:
test-postgresql:
env_file: environment.env
environment:
- PYTHONDONTWRITEBYTECODE=1
build:
dockerfile: docker/app/Dockerfile
context: .
entrypoint: pytest
depends_on:
- db
volumes:
- ./ThoughtsBackend:/opt/code
This is very similar to the test-sqlite service, but it adds the environment configuration in environment.env and adds a dependency on db. This means that docker-compose will start the db service, if not present.
You can now run the tests against the PostgreSQL database:
$ docker-compose run test-postgresql
Starting ch3_db_1 ... done
============== test session starts ====================
platform linux -- Python 3.6.8, pytest-4.6.0, py-1.8.0, pluggy-0.12.0 -- /opt/venv/bin/python3
cachedir: .pytest_cache
rootdir: /opt/code, inifile: pytest.ini
plugins: flask-0.14.0
collected 17 items
tests/test_thoughts.py::test_create_me_thought PASSED [ 5%]
...
tests/test_token_validation.py::test_valid_token_header PASSED [100%]
===== 17 passed, 177 warnings in 2.14 seconds ===
$
This environment file will be useful for any service that needs to connect to the database, such as deploying the service locally.