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.