Skip to content
Advertisement

Flask testing with pytest, ENV is set to production?

I have built a flask app and i would like to create a test suite. Reading around it looks like pytest is the way to go; however, I am finding it very difficult to understand how to get going, I have looked at https://flask.palletsprojects.com/en/2.0.x/testing/ but am struggling to relate it to my app.

my project has a run.py at its base:

from wahoo_connect import init_app, db
from flask_migrate import Migrate

#run the app
app = init_app()
migrate = Migrate(app, db)

this is run using flask run and the .flaskenv sets the mode

FLASK_APP=run.py
#FLASK_ENV=production
FLASK_ENV=development

I have an application factory set up:

"""Main entry point into App"""
#import libraries
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
from flask_mail import Mail
#from flask_caching import Cache
from flask_session import Session
from os import path, mkdir
import logging
from logging.handlers import SMTPHandler, RotatingFileHandler

"""Flask Global Variables"""
#database connection
db = SQLAlchemy()
migrate = Migrate()

#login manager
login = LoginManager()
login.login_view = 'auth_bp.login'
login.login_message = 'Please log in to access this page.'
login.login_message_category = "info"

#email
mail = Mail()

#cache
#cache = Cache()

#session
sess = Session()

#initialise app
def init_app():
    """Initialize the core application."""
    app = Flask(__name__)
    print(app.config['ENV'])
    if app.config['ENV'] == 'production':
        app.config.from_object('wahoo_connect.config.ProductionConfig')
    elif app.config['ENV'] == 'development':
        app.config.from_object('wahoo_connect.config.DevelopmentConfig')
    elif app.config['ENV'] == 'testing':
        app.config.from_object('wahoo_connect.config.TestingConfig')

    # Initialize Plugins
    db.init_app(app)
    migrate.init_app(app, db)
    login.init_app(app)
    mail.init_app(app)
#    cache.init_app(app)
    sess.init_app(app)

    with app.app_context():
        # Import and Register Blueprints
        from wahoo_connect.errors.views import errors_bp
        from wahoo_connect.auth.views import auth_bp
        from wahoo_connect.profile.views import profile_bp
        from wahoo_connect.wahoo.views import wahoo_bp
        from wahoo_connect.home.views import home_bp
        app.register_blueprint(errors_bp)
        app.register_blueprint(auth_bp, url_prefix='/auth')
        app.register_blueprint(profile_bp)
        app.register_blueprint(wahoo_bp, url_prefix='/wahoo')
        app.register_blueprint(home_bp)

        if not app.debug:
            #log to email
            if app.config['MAIL_SERVER']:
                auth = None
            if app.config['MAIL_USERNAME'] or app.config['MAIL_PASSWORD']:
                auth = (app.config['MAIL_USERNAME'], app.config['MAIL_PASSWORD'])
            secure = None
            if app.config['MAIL_USE_TLS']:
                secure = ()
            mail_handler = SMTPHandler(
                mailhost=(app.config['MAIL_SERVER'], app.config['MAIL_PORT']),
                fromaddr=app.config['MAIL_USERNAME'],
                toaddrs=app.config['ADMINS'],
                subject='Error in Wahoo-Connect',
                credentials=auth, secure=secure)
            mail_handler.setLevel(logging.ERROR)
            app.logger.addHandler(mail_handler)

            #log to file - heroku
            if app.config['LOG_TO_STDOUT']:
                stream_handler = logging.StreamHandler()
                stream_handler.setLevel(logging.INFO)
                app.logger.addHandler(stream_handler)
            else:
                logdir = path.dirname(app.instance_path) + '/logs'
                if not path.exists(logdir):
                    mkdir(logdir)
                file_handler = RotatingFileHandler(logdir + '/wahoo-connect.log', 
                    maxBytes=10240, backupCount=10)
                file_handler.setFormatter(logging.Formatter(
                    '%(asctime)s %(levelname)s: %(message)s '
                    '[in %(pathname)s:%(lineno)d]'))
                file_handler.setLevel(logging.INFO)
                app.logger.addHandler(file_handler)

            app.logger.setLevel(logging.INFO)
            app.logger.info('Wahoo-Connect Startup')

        return app

from wahoo_connect import models

Everything works and I can run the app on a production server. I am trying to get started with pytest and have set up conftest.py:

import pytest
from wahoo_connect import init_app
from wahoo_connect.models import User

@pytest.fixture(scope="session")
def app():
    app = init_app()
    return app

this always runs the app in production mode, how do I get it to run in testing mode so that it uses the correct config from config.py

"""Flask configuration."""
from os import environ, path
from dotenv import load_dotenv
from datetime import timedelta

#basedir = path.abspath(path.dirname(__file__))
basedir = path.dirname(path.dirname(path.abspath(__file__)))
load_dotenv(path.join(basedir, '.env'))

class Config(object):
    """Base config."""
    DEBUG = False
    TESTING = False
    SECRET_KEY = environ.get('SECRET_KEY') or 'topsecretkey'
    STATIC_FOLDER = 'static'
    TEMPLATES_FOLDER = 'templates'
    SESSION_COOKIE_SECURE = True

    #Database
    SQLALCHEMY_DATABASE_URI = environ.get('DATABASE_URL') or 
        'sqlite:///' + path.join(basedir, 'app.db')
    #fix for heroku postgres db
    if SQLALCHEMY_DATABASE_URI.startswith("postgres://"):
        SQLALCHEMY_DATABASE_URI = SQLALCHEMY_DATABASE_URI.replace("postgres://", "postgresql://", 1)
    SQLALCHEMY_TRACK_MODIFICATIONS = False

    #Wahoo
    WAHOO_CLIENT_ID=environ.get('WAHOO_CLIENT_ID')
    WAHOO_CLIENT_SECRET=environ.get('WAHOO_CLIENT_SECRET')
    WAHOO_CLIENT_REDIRECT=environ.get('WAHOO_CLIENT_REDIRECT')

    #Email
    MAIL_SERVER = environ.get('MAIL_SMTP_SERVER')
    MAIL_PORT = int(environ.get('MAIL_SMTP_PORT') or 25)
    MAIL_USERNAME = environ.get('MAIL_SMTP_LOGIN')
    MAIL_PASSWORD = environ.get('MAIL_SMTP_PASSWORD')
    MAIL_USE_TLS = environ.get('MAIL_USE_TLS') is not None
    ADMINS = ['martyndwheeler@gmail.com']

    #Logging
    LOG_TO_STDOUT = environ.get('LOG_TO_STDOUT') or None

    # Flask-Caching related configs
    CACHE_TYPE = 'FileSystemCache'
    CACHE_DIR = environ.get('CACHE_DIR') or None
    CACHE_DEFAULT_TIMEOUT = 300
    CACHE_THRESHOLD = 100

    # Flask-Session related configs
    SESSION_PERMANENT = False
    PERMANENT_SESSION_LIFETIME = timedelta(minutes=30)
    SESSION_USE_SIGNER = True
    SESSION_TYPE = "filesystem"
    SESSION_FILE_DIR = environ.get('SESSION_FILE_DIR')
    SESSION_FILE_THRESHOLD = 100

class ProductionConfig(Config):
    pass

class DevelopmentConfig(Config):
    DEBUG = True
    SESSION_COOKIE_SECURE = False

class TestingConfig(Config):
    TESTING = True
    SESSION_COOKIE_SECURE = False

If there is a good tutorial that I have missed I’d be pleased to hear.

Advertisement

Answer

What is Dotenv

Use Dotenv package

#test.py
from os import environ

print(environ.get("ENV_VAR")) # Output : test
#.env
ENV_VAR=test

How to use it in youre case

Option 1

You can use this to store an Boolean in .env file and read it to define wich mode you are running. be careful as env variables get read as string. if you wanna use boolean you need to parse them from the string.

Option 2

An other option is to store in env file which config you wanna use and make an if else tree in python script:

if os.environ.get("CONFIG") == "1":
    app.config.from_object("config.conf1")
elif os.environ.get("CONFIG") == "2":
    app.config.from_object("config.conf2")
else:
    app.config.from_object("config.default")

Why use Dotenv

The advantage of using environment variables is that you can ignore them in git and every time the server is seetup in linux or docker all settings can be managed from one file. also does the standart server admin know bash script and env files but not necesseraly python.

User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement