I am trying to get the python testcontainer working with marshmallow to run a test but I don't seem to be able to get it working. I always get the connection refused when I run the test. I have created a sqlalchemy engine and tested it, and this works fine but when passing the testcontainer connection string as a marshmallow config, it just doesn't work. Below is my base test class.
class BaseTestCase(TestCase):
base_url = '/api/v1'
def create_app(self):
config = MySqlContainer('mysql:8.0.19')
with config as mysql:
print(mysql.get_connection_url())
e = sqlalchemy.create_engine(mysql.get_connection_url())
result = e.execute("select version()")
for row in result:
print("Printing::::::::::::::::::::::" + str(row))
result.close()
logging.getLogger('connexion.operation').setLevel('ERROR')
connex_app = connexion.App(__name__, specification_dir='../../api/')
connex_app.app.json_encoder = JSONEncoder
connex_app.add_api('static/openapi.yaml')
app = connex_app.app
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = mysql.get_connection_url()
app.config['LOG_LEVEL'] = 'DEBUG'
bcrypt.init_app(app)
db.init_app(app)
ma.init_app(app)
print("Finished setting up")
return app
I can get this to work when I use sqlite as the connection string.
class BaseTestCase(TestCase):
base_url = '/api/v1'
def create_app(self):
class Config:
PORT = 5000
SQLALCHEMY_TRACK_MODIFICATIONS = False
FLASK_ENV = 'local'
SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'
LOG_LEVEL = 'DEBUG'
logging.getLogger('connexion.operation').setLevel('ERROR')
connex_app = connexion.App(__name__, specification_dir='../../api/')
connex_app.app.json_encoder = JSONEncoder
connex_app.add_api('static/openapi.yaml')
app = connex_app.app
app.config.from_object(Config)
bcrypt.init_app(app)
db.init_app(app)
ma.init_app(app)
print("Finished setting up test")
return app
Any help will be much appreciated.
Paul
I decided to use testcontainers.compose and the connection string seems to work.
class BaseTestCase(TestCase):
base_url = '/api/v1'
def create_app(self):
self.compose = testcontainers.compose.DockerCompose(".")
self.compose.start()
time.sleep(10)
class Config:
PORT = 3306
SQLALCHEMY_TRACK_MODIFICATIONS = False
FLASK_ENV = 'local'
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:password#0.0.0.0:3306/test-db'
LOG_LEVEL = 'DEBUG'
logging.getLogger('connexion.operation').setLevel('ERROR')
connex_app = connexion.App(__name__, specification_dir='../../api/')
connex_app.app.json_encoder = JSONEncoder
connex_app.add_api('static/openapi.yaml')
app = connex_app.app
app.config.from_object(Config)
bcrypt.init_app(app)
db.init_app(app)
ma.init_app(app)
print("Finished setting up test")
return app
Related
I have a FastAPI app and I need to populate a testing DB with some data needed for testing using pyTest.
This is my code for testing DB in conftest.py:
SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base.metadata.drop_all(bind=engine)
Base.metadata.create_all(bind=engine)
def override_get_db():
"""Redirect request to use testing DB."""
try:
db = TestingSessionLocal()
yield db
finally:
db.close()
app.dependency_overrides[get_db] = override_get_db
#pytest.fixture(scope="module")
def test_client():
"""Test client initiation for all tests."""
client = TestClient(app)
yield client
I need to implement something like this:
#pytest.fixture(scope="function")
def test_data(get_db):
waiter = Waiter(
id=1,
username="User name",
password="$12$BQhTQ6/OLAmkG/LU6G2J2.ngFk6EI9hBjFNjeTnpj2eVfQ3DCAtT.",
)
dish = Dish(
id=1,
name="Some dish",
description="Some description",
image_url="https://some.io/fhjhd.jpg",
cost=1.55,
)
get_db.add(waiter)
get_db.add(dish)
get_db.commit()
And here is a test:
def test_get_waiter(test_client, waiter_data):
"""Test Get a waiter by id."""
response = test_client.get("/waiters/1")
assert response.status_code == 200
But in this case I get fixture 'get_db' not found. How do I?
you must set a fixture with get_db name:
#pytest.fixture(name="get_db")
def get_test_db():
return override_get_db()
I suggest you have a conftest.py and put this config into it.
I've handled it in this way in conftest.py:
#pytest.fixture(scope="module")
def db_session():
"""Fixture to connect with DB."""
connection = engine.connect()
session = TestingSessionLocal(bind=connection)
yield session
session.close()
#pytest.fixture(scope="function")
def test_db_data(db_session):
waiter = Waiter(
username="User name",
password="$12$BQhTQ6/OLAmkG/LU6G2J2.ngFk6EI9hBjFNjeTnpj2eVfQ3DCAtT.",
)
db_session.add(waiter)
db_session.commit()
yield db_session
I am first time trying flask application factory pattern and pytest framework together.
I started with a basic sanity test for the sqlite db backend and, although the tests are working fine so far and I see test db file created successfully, the falsk_sqlalchemy is telling me that it doesn't have a db backend defined.
I tried to find the problem with pdb and the interactive console - everything looks normal. It looks like it is somehow related to
could anyone help me understand where the problem is?
(venv) C:\Users\dv\PycharmProjects\ste-speach-booking>python -m pytest tests/
=========================== test session starts ============================
platform win32 -- Python 3.6.8, pytest-5.1.1, py-1.8.0, pluggy-0.12.0
rootdir: C:\Users\dv\PycharmProjects\ste-speach-booking
collected 3 items
tests\test_models.py ... [100%]
============================= warnings summary =============================
tests/test_models.py::test_init
C:\Users\d837758\PycharmProjects\ste-speach-booking\venv\lib\site-packages\flask_sqlalchemy\__init__.py:814: UserWarning: Neither SQLALCHEMY_DATABASE_URI nor SQLALCHEMY_BINDS is set. Defaulting SQLALCHEMY_DATABASE_URI to "sqlite:///:memory:".
'Neither SQLALCHEMY_DATABASE_URI nor SQLALCHEMY_BINDS is set. '
initial tests in the test_models:
import pytest
import src.models
import datetime
def test_ActionTypes(db):
actiontype1 = src.models.Act_types(action_tyoe='workshop')
db.session.add(actiontype1)
db.session.commit()
actiontype2 = src.models.Act_types(action_tyoe='speech')
db.session.add(actiontype2)
db.session.commit()
count = db.session.query(src.models.Act_types).count()
assert count is 2
def test_meeting_creation(db):
meeting = src.models.Meeting(
_date = datetime.datetime.strptime('2018-12-19', "%Y-%m-%d"),
)
db.session.add(meeting)
db.session.commit()
conftest fixture for the db:
import os
import pytest
import src.config
from src import create_app
from src import db as _db
#pytest.fixture(scope='session')
def db():
"""Session-wide test database."""
TESTDB_PATH = src.config.testDB
print(TESTDB_PATH)
if os.path.exists(TESTDB_PATH):
os.unlink(TESTDB_PATH)
app = create_app(config=src.config.TestingConfig)
with app.app_context():
_db.create_all()
yield _db
_db.drop_all()
os.unlink(TESTDB_PATH)
app factory:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app(config=None):
"""Construct the core application."""
app = Flask(__name__, instance_relative_config=True)
db.init_app(app)
if config is None:
app.config.from_object(config.BaseConfig)
else:
app.config.from_object(config)
with app.app_context():
# Imports
from . import routes
db.create_all()
return app
config.py:
basedir = os.path.abspath(os.path.dirname(__file__))
baseDB = os.path.join(basedir, 'app.db')
devDB = os.path.join(basedir, 'dev_app.db')
testDB = os.path.join(basedir, 'testing_app.db')
class BaseConfig(object):
DEBUG = False
TESTING = False
SECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-guess'
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + baseDB
SQLALCHEMY_TRACK_MODIFICATIONS = False
class TestingConfig(BaseConfig):
DEBUG = False
TESTING = True
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + testDB
The issue is with the order of configuration of the components of your application in create_app().
When you call db.init_app(app) the first operations it performs are (source):
if (
'SQLALCHEMY_DATABASE_URI' not in app.config and
'SQLALCHEMY_BINDS' not in app.config
):
warnings.warn(
'Neither SQLALCHEMY_DATABASE_URI nor SQLALCHEMY_BINDS is set. '
'Defaulting SQLALCHEMY_DATABASE_URI to "sqlite:///:memory:".'
)
Recognize that warning?
Immediately it looks in app.config for required configurations. The method goes on to either accept the supplied configuration from the app or set a default, in this case the default is the in memory database.
In your implementation of create_app() the call to db.init_app() comes before the app itself is configured, with this:
db.init_app(app)
if config is None:
app.config.from_object(config.BaseConfig)
else:
app.config.from_object(config)
Until app.config is populated, none of the SQLALCHEMY_ prefixed configurations exist on the app and so when db.init_app() goes looking for them, they aren't found and the defaults are used. Moving the config of db to after the config of the app fixes the issue:
if config is None:
app.config.from_object(config.BaseConfig)
else:
app.config.from_object(config)
db.init_app(app)
This is quite similar to this question, however I think yours is a better example of a typical setup (scope of create_app() and configuration method) so worth answering.
Ensure the app.config dictionary has the following:
app = Flask(__name__) # The URI config should be initialized after flask
['SQLALCHEMY_DATABASE_URI'] = 'to your database string'
then:
db = SQAlchemy(app)
was having the same issue because I had the Flask initialization after the database Uri connection.
Problem: I am new to creating a website on the flask and my code is running smoothly without any error, but I can't see the database file on my working directory or in my Project and I dont know what is the reason why the database file is not showing or creating in my directory. Can someone help me?
My directory
Project
|
|__flask-session
|__ST_website
|____flask-session
|____static
|____templates
|______ __init__.py
|______ database.py
|______ primary.py
|______ user_identification.py
|__venv(library root)
|__run.py
(|__ST_database.db # wanted to create)
init.py
import os
from flask_session import Session
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from datetime import timedelta
# Database
db = SQLAlchemy()
DB_NAME = "ST_database.db"
UPLOAD_FOLDER = 'ST_website/static/csv-files' # Upload Folder
def create_app():
""" Creating an App"""
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hi'
app.config['SQLALCHEMY_DATABASE_URL'] = 'sqlite:///ST_database.db'
app.config["SQLAlCHEMY_TRACK_MODIFICATIONS"] = False # Handling Warnings
app.config["SESSION_TYPE"] = "filesystem"
app.permanent_session_lifetime = timedelta(minutes=20) # How long will the data last.
db.init_app(app)
Session(app)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER # Upload file
# file name Variable name
from .primary import primary
from .user_identification import user_identi
# Registering Blueprints
app.register_blueprint(user_identi, url_prefix='/')
app.register_blueprint(primary, url_prefix='/')
login_man = LoginManager()
login_man.login_view = 'user_identi.login' # Directing here if the user is not log-in
login_man.init_app(app) # telling Login manager what app will use
from .database import User, UserData
create_database(app)
#login_man.user_loader
def load_user(id):
"""Load the user"""
return User.query.get(int(id)) # automatically get the Primary Key
return app
def create_database(application):
db_filename = f'ST_website/{DB_NAME}'
if not os.path.exists(db_filename):
db.create_all(app=application)
print('Created Database!')
database.py
from . import db
from flask_login import UserMixin
from sqlalchemy.sql import func
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(150), unique=True)
name = db.Column(db.String(150))
password = db.Column(db.String(150))
user_data = db.relationship('UserData')
class UserData(db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(150))
filename = db.Column(db.String(150))
filepath = db.Column(db.String(150))
date_of_upload = db.Column(db.DateTime(timezone=True), default=func.now())
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
run.py
from ST_website import create_app
app = create_app()
print(app)
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)
Terminal
D:\Project\venv\lib\site-packages\flask_sqlalchemy\__init__.py:851: UserWarning: Neither SQLALCHEMY_DATABASE_URI nor SQLALCHEMY_BINDS is set. Defaulting SQLALCHEMY_DATABASE_URI to "sqlite:///:memory:".
warnings.warn(
D:\Project\venv\lib\site-packages\flask_sqlalchemy\__init__.py:872:FSADeprecationWarning: SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future. Set it to True or False to suppress this warning.
warnings.warn(FSADeprecationWarning(
Created Database!
<Flask 'ST_website'>
* Serving Flask app 'ST_website' (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: on
* Restarting with stat
D:\Project\venv\lib\site-packages\flask_sqlalchemy\__init__.py:851: UserWarning: Neither SQLALCHEMY_DATABASE_URI nor SQLALCHEMY_BINDS is set. Defaulting SQLALCHEMY_DATABASE_URI to "sqlite:///:memory:".
warnings.warn(
D:\Project\venv\lib\site-packages\flask_sqlalchemy\__init__.py:872:FSADeprecationWarning: SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future. Set it to True or False to suppress this warning.
warnings.warn(FSADeprecationWarning(
* Debugger is active!
* Debugger PIN: 412-312-823
Created Database!
<Flask 'ST_website'>
* Running on all addresses.
WARNING: This is a development server. Do not use it in a production deployment.
* Running on http://192.168.1.10:5000/ (Press CTRL+C to quit)
In your init.py file, please change this line.
app.config['SQLALCHEMY_DATABASE_URL'] = 'sqlite:///ST_database.db'
to,
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///ST_database.db'
I am first time trying flask application factory pattern and pytest framework together.
I started with a basic sanity test for the sqlite db backend and, although the tests are working fine so far and I see test db file created successfully, the falsk_sqlalchemy is telling me that it doesn't have a db backend defined.
I tried to find the problem with pdb and the interactive console - everything looks normal. It looks like it is somehow related to
could anyone help me understand where the problem is?
(venv) C:\Users\dv\PycharmProjects\ste-speach-booking>python -m pytest tests/
=========================== test session starts ============================
platform win32 -- Python 3.6.8, pytest-5.1.1, py-1.8.0, pluggy-0.12.0
rootdir: C:\Users\dv\PycharmProjects\ste-speach-booking
collected 3 items
tests\test_models.py ... [100%]
============================= warnings summary =============================
tests/test_models.py::test_init
C:\Users\d837758\PycharmProjects\ste-speach-booking\venv\lib\site-packages\flask_sqlalchemy\__init__.py:814: UserWarning: Neither SQLALCHEMY_DATABASE_URI nor SQLALCHEMY_BINDS is set. Defaulting SQLALCHEMY_DATABASE_URI to "sqlite:///:memory:".
'Neither SQLALCHEMY_DATABASE_URI nor SQLALCHEMY_BINDS is set. '
initial tests in the test_models:
import pytest
import src.models
import datetime
def test_ActionTypes(db):
actiontype1 = src.models.Act_types(action_tyoe='workshop')
db.session.add(actiontype1)
db.session.commit()
actiontype2 = src.models.Act_types(action_tyoe='speech')
db.session.add(actiontype2)
db.session.commit()
count = db.session.query(src.models.Act_types).count()
assert count is 2
def test_meeting_creation(db):
meeting = src.models.Meeting(
_date = datetime.datetime.strptime('2018-12-19', "%Y-%m-%d"),
)
db.session.add(meeting)
db.session.commit()
conftest fixture for the db:
import os
import pytest
import src.config
from src import create_app
from src import db as _db
#pytest.fixture(scope='session')
def db():
"""Session-wide test database."""
TESTDB_PATH = src.config.testDB
print(TESTDB_PATH)
if os.path.exists(TESTDB_PATH):
os.unlink(TESTDB_PATH)
app = create_app(config=src.config.TestingConfig)
with app.app_context():
_db.create_all()
yield _db
_db.drop_all()
os.unlink(TESTDB_PATH)
app factory:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app(config=None):
"""Construct the core application."""
app = Flask(__name__, instance_relative_config=True)
db.init_app(app)
if config is None:
app.config.from_object(config.BaseConfig)
else:
app.config.from_object(config)
with app.app_context():
# Imports
from . import routes
db.create_all()
return app
config.py:
basedir = os.path.abspath(os.path.dirname(__file__))
baseDB = os.path.join(basedir, 'app.db')
devDB = os.path.join(basedir, 'dev_app.db')
testDB = os.path.join(basedir, 'testing_app.db')
class BaseConfig(object):
DEBUG = False
TESTING = False
SECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-guess'
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + baseDB
SQLALCHEMY_TRACK_MODIFICATIONS = False
class TestingConfig(BaseConfig):
DEBUG = False
TESTING = True
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + testDB
The issue is with the order of configuration of the components of your application in create_app().
When you call db.init_app(app) the first operations it performs are (source):
if (
'SQLALCHEMY_DATABASE_URI' not in app.config and
'SQLALCHEMY_BINDS' not in app.config
):
warnings.warn(
'Neither SQLALCHEMY_DATABASE_URI nor SQLALCHEMY_BINDS is set. '
'Defaulting SQLALCHEMY_DATABASE_URI to "sqlite:///:memory:".'
)
Recognize that warning?
Immediately it looks in app.config for required configurations. The method goes on to either accept the supplied configuration from the app or set a default, in this case the default is the in memory database.
In your implementation of create_app() the call to db.init_app() comes before the app itself is configured, with this:
db.init_app(app)
if config is None:
app.config.from_object(config.BaseConfig)
else:
app.config.from_object(config)
Until app.config is populated, none of the SQLALCHEMY_ prefixed configurations exist on the app and so when db.init_app() goes looking for them, they aren't found and the defaults are used. Moving the config of db to after the config of the app fixes the issue:
if config is None:
app.config.from_object(config.BaseConfig)
else:
app.config.from_object(config)
db.init_app(app)
This is quite similar to this question, however I think yours is a better example of a typical setup (scope of create_app() and configuration method) so worth answering.
Ensure the app.config dictionary has the following:
app = Flask(__name__) # The URI config should be initialized after flask
['SQLALCHEMY_DATABASE_URI'] = 'to your database string'
then:
db = SQAlchemy(app)
was having the same issue because I had the Flask initialization after the database Uri connection.
I'm trying to do unit testing of my Flask web app. I'm use a pattern I saw in a Udemy class on Flask and a pattern similar to the Flask Mega-Tutorial online (http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-vii-unit-testing). The problem I'm having is that the test does not actual create it's own database -- rather it uses the production database and messes it up.
Here's what tests.py script looks like:
import os,sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
basedir = os.path.abspath(os.path.dirname(__file__))
import unittest
from myapp import app, db
from user.models import User
class UserTest(unittest.TestCase):
def setUp(self):
self.db_uri = 'sqlite:///' + os.path.join(basedir, 'test.db')
app.config['TESTING'] = True
app.config['WTF_CSRF_ENABLED'] = False
app.config['SQL_ALCHEMY_DATABASE_URI'] = self.db_uri
self.app = app.test_client()
db.create_all()
def tearDown(self):
db.session.remove()
db.drop_all()
def test_models(self):
#Create a customer user
user = User("John Doe", "jdoe#jdoe.com", "jdoe", "password", is_consultant=False)
db.session.add(user)
db.session.commit()
#Create 2 consultant users
user1 = User("Jane Doe", "janedoe#gg.com", "janedoe", "password", is_consultant=True)
db.session.add(user1)
user2 = User("Nikola Tesla", "nikola#tesla.com", "nikola", "password", is_consultant=True)
db.session.add(user2)
db.session.commit()
#Check that all users exist
assert len(User.query.all()) is 3
My app init is in the same folder and looks like so:
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.migrate import Migrate
from flask.ext.login import LoginManager
app = Flask(__name__)
app.config.from_object('config')
db = SQLAlchemy(app)
# flask-login extension setup
login_manager = LoginManager()
login_manager.init_app(app)
# migration setup
migrate = Migrate(app, db)
from user import views
I don't understand what is going on. It never creates the test.db SQLite database. It always creates the app.db production database. And if it's there, it totally messes up the database is there. Afterwards if I do python manage.py runserver -- it doesn't work anymore. It says table not found. Because the teardown dropped all the tables. What is going on? And how do I fix it?
Omigod I figured it out. I was setting the wrong key for the database URI. It should be: app.config['SQLALCHEMY_DATABASE_URI'] = self.db_uri.
So everything is fine. Just do:
class UserTest(unittest.TestCase):
def setUp(self):
self.db_uri = 'sqlite:///' + os.path.join(basedir, 'test.db')
app.config['TESTING'] = True
app.config['WTF_CSRF_ENABLED'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = self.db_uri
self.app = app.test_client()
db.create_all()
and everything works as intended.
I checked what was going on by putting a break-point in the tests and seeing what app.config was -- and I saw that there was both a SQL_ALCHEMY_DATABASE_URI key (which doesn't do anything and I was setting) and a SQLALCHEMY_DATABASE_URI key (this is the one that matters).