当前位置: 首页 > 软件库 > Web应用开发 > Web框架 >

pytest-flask-sqlalchemy

授权协议 MIT License
开发语言 Python
所属分类 Web应用开发、 Web框架
软件类型 开源软件
地区 不详
投 递 者 弓磊
操作系统 跨平台
开源组织
适用人群 未知
 软件概览

pytest-flask-sqlalchemy

PyPI - Python Version

A pytest plugin providing fixtures for running tests intransactions using Flask-SQLAlchemy.

Contents

Motivation

Inspired by Django's built-in support for transactionaltests, this pluginseeks to provide comprehensive, easy-to-use Pytest fixtures for wrapping tests indatabase transactions for Flask-SQLAlchemyapps. The goal is to make testing stateful Flask-SQLAlchemy applications easier byproviding fixtures that permit the developer to make arbitrary database updateswith the confidence that any changes made during a test will roll back once the test exits.

Quick examples

Use the db_session fixture to make database updates that won't persist beyondthe body of the test:

def test_a_transaction(db_session):
   row = db_session.query(Table).get(1) 
   row.name = 'testing'

   db_session.add(row)
   db_session.commit()

def test_transaction_doesnt_persist(db_session):
   row = db_session.query(Table).get(1) 
   assert row.name != 'testing'

The db_engine fixture works the same way, but copies the API ofSQLAlchemy's Engineobject:

def test_a_transaction_using_engine(db_engine):
    with db_engine.begin() as conn:
        row = conn.execute('''UPDATE table SET name = 'testing' WHERE id = 1''')

def test_transaction_doesnt_persist(db_engine):
    row_name = db_engine.execute('''SELECT name FROM table WHERE id = 1''').fetchone()[0]
    assert row_name != 'testing'

Use configuration properties tomock database connections in an app and enforce nested transactions,allowing any method from the codebase to run inside a test with the assurancethat any database changes made will be rolled back at the end of the test:

# In setup.cfg

[tool:pytest]
mocked-sessions=database.db.session
mocked-engines=database.engine
# In database.py

db = flask_sqlalchemy.SQLAlchemy()
engine = sqlalchemy.create_engine('DATABASE_URI')
# In models.py

class Table(db.Model):
    __tablename__ = 'table'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80))

    def set_name(new_name):
        self.name = new_name
        db.session.add(self)
        db.session.commit()
# In tests/test_set_name.py

def test_set_name(db_session):
    row = db_session.query(Table).get(1)
    row.set_name('testing')
    assert row.name == 'testing'

def test_transaction_doesnt_persist(db_session):
   row = db_session.query(Table).get(1) 
   assert row.name != 'testing'

Usage

Installation

From PyPi

Install using pip:

pip install pytest-flask-sqlalchemy

Once installed, pytest will detect the plugin automatically during test collection.For basic background on using third-party plugins with pytest, see the pytestdocumentation.

Development version

Clone the repo from GitHub and switch into the new directory:

git clone git@github.com:jeancochrane/pytest-flask-sqlalchemy.git
cd pytest-flask-sqlalchemy

You can install using pip:

pip install .

Supported backends

So far, pytest-flask-sqlalchemy has been most extensively tested againstPostgreSQL 9.6. It should theoretically work with any backend that is supportedby SQLAlchemy, but Postgres is the only backend that is currently tested by thetest suite.

Official support for SQLite and MySQL is planned for a futurerelease.In the meantime, if you're using one of those backends and you run in toproblems, we would greatly appreciate your help! Open anissue ifsomething isn't working as you expect.

Configuration

Conftest setup

This plugin assumes that a fixture called _db has beendefined in the root conftest file for your tests. The _db fixture shouldexpose access to a valid SQLAlchemy Session object that can interact with your database,for example via the SQLAlchemy initialization classthat configures Flask-SQLAlchemy.

The fixtures in this plugin depend on this _db fixture to access yourdatabase and create nested transactions to run tests in. You must definethis fixture in your conftest.py file for the plugin to work.

An example setup that will produce a valid _db fixture could look like this(this example comes from the test setup for this repo):

@pytest.fixture(scope='session')
def database(request):
    '''
    Create a Postgres database for the tests, and drop it when the tests are done.
    '''
    pg_host = DB_OPTS.get("host")
    pg_port = DB_OPTS.get("port")
    pg_user = DB_OPTS.get("username")
    pg_db = DB_OPTS["database"]

    init_postgresql_database(pg_user, pg_host, pg_port, pg_db)

    @request.addfinalizer
    def drop_database():
        drop_postgresql_database(pg_user, pg_host, pg_port, pg_db, 9.6)


@pytest.fixture(scope='session')
def app(database):
    '''
    Create a Flask app context for the tests.
    '''
    app = Flask(__name__)

    app.config['SQLALCHEMY_DATABASE_URI'] = DB_CONN

    return app


@pytest.fixture(scope='session')
def _db(app):
    '''
    Provide the transactional fixtures with access to the database via a Flask-SQLAlchemy
    database connection.
    '''
    db = SQLAlchemy(app=app)

    return db

Alternatively, if you already have a fixture that sets up database access foryour tests, you can define _db to return that fixture directly:

@pytest.fixture(scope='session')
def database():
    # Set up all your database stuff here
    # ...
    return db

@pytest.fixture(scope='session')
def _db(database):
    return database

Test configuration

This plugin allows you to configure a few different properties in asetup.cfg test configuration file in order to handle the specific database connection needsof your app. For basic background on setting up pytest configuration files, seethe pytest docs.

All three configuration properties (mocked-engines,mocked-sessions, and mocked-sessionmakers)work by patchingone or more specified objects during a test, replacing them with equivalent objects whosedatabase interactions will run inside of a transaction and ultimately berolled back when the test exits. Using these patches, you can call methods fromyour codebase that alter database state with the knowledge that no changes will persistbeyond the body of the test.

The configured patches are only applied in tests where a transactional fixture(either db_session or db_engine) is includedin the test function arguments.

mocked-engines

The mocked-engines property directs the plugin to patchobjects in your codebase, typically SQLAlchemy Engineinstances, replacing them with the db_engine fixture such thatany database updates performed by the objects get rolled back at the end ofthe test.

The value for this property should be formatted as a whitespace-separated listof standard Python import paths, like database.engine. This property is optional.

Example:

# In database.py

engine = sqlalchemy.create_engine(DATABASE_URI)
# In setup.cfg

[tool:pytest]
mocked-engines=database.engine

To patch multiple objects at once, separate the paths with a whitespace:

# In setup.cfg

[tool:pytest]
mocked-engines=database.engine database.second_engine

mocked-sessions

The mocked-sessions property directs the plugin to patchobjects in your codebase, typically SQLAlchemy Sessioninstances, replacing them with the db_session fixture such thatany database updates performed by the objects get rolled back at the end ofthe test.

The value for this property should be formatted as a whitespace-separated listof standard Python import paths, like database.db.session. This property is optional.

Example:

# In database.py

db = SQLAlchemy()
# In setup.cfg

[tool:pytest]
mocked-sessions=database.db.session

To patch multiple objects at once, separate the paths with a whitespace:

# In setup.cfg

[tool:pytest]
mocked-sessions=database.db.session database.second_db.session

mocked-sessionmakers

The mocked-sessionmakers property directs the plugin to patchobjects in your codebase, typically instances of SQLAlchemy's sessionmakerfactory,replacing them with a mocked class that will return the transactionaldb_session fixture. This can be useful if you havepre-configured instances of sessionmaker objects that you import in the codeto spin up sessions on the fly.

The value for this property should be formatted as a whitespace-separated listof standard Python import paths, like database.WorkerSessionmaker. This property is optional.

Example:

# In database.py

WorkerSessionmaker = sessionmaker()
[tool:pytest]
mocked-sessionmakers=database.WorkerSessionmaker

To patch multiple objects at once, separate the paths with a whitespace.

[tool:pytest]
mocked-sessionmakers=database.WorkerSessionmaker database.SecondWorkerSessionmaker

Writing transactional tests

Once you have your conftest file set up and you've overrided thenecessary connectables in your test configuration, you're readyto write some transactional tests. Simply import one of the module's transactionalfixtures in your test signature, and the test will be wrapped in a transaction.

Note that by default, tests are only wrapped in transactions if they import one ofthe transactional fixtures provided by this module. Tests that do notimport the fixture will interact with your database without opening a transaction:

# This test will be wrapped in a transaction.
def transactional_test(db_session):
    ...
    
# This test **will not** be wrapped in a transaction, since it does not import a
# transactional fixture.
def non_transactional_test():
    ...

The fixtures provide a way for you to control which tests require transactions andwhich don't. This is often useful, since avoiding transaction setup can speed uptests that don't interact with your database.

For more information about the transactional fixtures provided by this module, read onto the fixtures section. For guidance on how to automatically enabletransactions without having to specify fixtures, see the section on enabling transactionswithout fixtures.

Fixtures

This plugin provides two fixtures for performing database updates inside nestedtransactions that get rolled back at the end of a test: db_session anddb_engine. The fixtures provide similar functionality, butwith different APIs.

db_session

The db_session fixture allows you to perform direct updates that will berolled back when the test exits. It exposes the same API as SQLAlchemy'sscoped_session object.

Including this fixture as a function argument of a test will activate any mocks that are definedby the configuration properties mocked-engines, mocked-sessions,or mocked-sessionmakers in the test configuration file forthe duration of that test.

Example:

def test_a_transaction(db_session):
   row = db_session.query(Table).get(1) 
   row.name = 'testing'

   db_session.add(row)
   db_session.commit()

def test_transaction_doesnt_persist(db_session):
   row = db_session.query(Table).get(1) 
   assert row.name != 'testing'

db_engine

Like db_session, the db_engine fixture allows you to perform direct updatesagainst the test database that will be rolled back when the test exits. It isan instance of Python's built-in MagicMockclass, with a spec set to match the API of SQLAlchemy'sEngine object.

Only a few Engine methods are exposed on this fixture:

  • db_engine.begin: begin a new nested transaction (API docs)
  • db_engine.execute: execute a raw SQL query (API docs)
  • db_engine.raw_connection: return a raw DBAPI connection (API docs)

Since db_engine is an instance of MagicMock with an Engine spec, othermethods of the Engine API can be called, but they will not perform any usefulwork.

Including this fixture as a function argument of a test will activate any mocks that are definedby the configuration properties mocked-engines, mocked-sessions,or mocked-sessionmakers in the test configuration file forthe duration of that test.

Example:

def test_a_transaction_using_engine(db_engine):
    with db_engine.begin() as conn:
        row = conn.execute('''UPDATE table SET name = 'testing' WHERE id = 1''')

def test_transaction_doesnt_persist(db_engine):
    row_name = db_engine.execute('''SELECT name FROM table WHERE id = 1''').fetchone()[0]
    assert row_name != 'testing'

Enabling transactions without fixtures

If you know you want to make all of your tests transactional, it can be annoying to haveto specify one of the fixtures in every test signature.

The best way to automatically enable transactions without having to include an extra fixturein every test is to wire up anautouse fixturefor your test suite. This can be as simple as:

# Automatically enable transactions for all tests, without importing any extra fixtures.
@pytest.fixture(autouse=True)
def enable_transactional_tests(db_session):
    pass

In this configuration, the enable_transactional_tests fixture will be automatically used inall tests, meaning that db_session will also be used. This way, all tests will be wrappedin transactions without having to explicitly require either db_session or enable_transactional_tests.

Development

Running the tests

To run the tests, start by installing a development version of the plugin thatincludes test dependencies:

pip install -e .[tests]

Next, export a database connection stringthat the tests can use (the database referenced by the string will be createdduring test setup, so it does not need to exist):

export TEST_DATABASE_URL=<db_connection_string>

Finally, run the tests using pytest:

pytest

Acknowledgements

This plugin was initially developed for testingDedupe.io, a web app for record linkage and entityresolution using machine learning. Dedupe.io is built and maintainedby DataMade.

The code is greatly indebted to Alex Michael,whose blog post "Delightful testing with pytest andFlask-SQLAlchemy" helpedestablish the basic approach on which this plugin builds.

Many thanks to Igor Ghisi, who donated the PyPipackage name. Igor had been working on a similar plugin and proposed combiningefforts. Thanks to Igor, the plugin name is much stronger.

Copyright

Copyright (c) 2019 Jean Cochrane and DataMade. Released under the MIT License.

Third-party copyright in this distribution is noted where applicable.

  • flask-sqlalchemy、pytest 的单元测试和事务自动回滚 使用 flask-sqlalchemy 做数据库时,单元测试可以帮助发现一些可能意想不到的问题,像 delete-cascade 、数据长度、多对多关联等等。如果使用 alembic 管理数据库版本,还可以写些跟迁移相关的单元测试。在团队中实现规范的单元测试,再配合 flake8 / pep8 之类的代码规范工具,有助于提高

  •    安装python3.6之后建立虚拟环境并启用,退出虚拟环境使用deactivate命令 1007 python3 -m venv venv #建立虚拟环境 1008 source venv/bin/activate #启用虚拟环境 1009 pip install pyodbc #虚拟环境下安装python扩展包 1010 pip install --upgrade pip

  •  #coding:utf-8 #SQLAlchemy 是一个 Python 包,其最底层包装了对数据库进入操作的统一接口,然后在最上层提供了对象关系映射(ORM)的实现。 from flask_sqlalchemy import SQLAlchemy #引入Flask类,Flask实现了一个WSGI对象 from flask import Flask import os p

  • 第一步app.py: # coding=utf-8 import urllib from flask import Flask from werkzeug.routing import BaseConverter from flask.ext.sqlalchemy import SQLAlchemy app = Flask(__name__) app.config['DEBUG'] = True

  • Flask-单元测试 敏捷开发(agile development) scrum 结对编程 测试驱动开发(TDD): Test driven development 单元测试(unit testing)是开发者自己编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为。 注意单元测试是开发人员自己负责 un

  • flask-sqlalchemy、pytest 的单元测试和事务自动回滚 flask-sqlalchemy.pytest 的单元测试和事务自动回滚 使用 flask-sqlalchemy 做数据库时,单元测试可以帮助发现一些可能意想不到的问题,像 delete-cascad ... Python单元测试框架之pytest -- 断言 对于测试来讲,不管是功能测试,自动化测试,还是单元测试.一般都会

  • requirements 文件 Python 项目中必须包含一个 requirements.txt 文件,用于记录所有依赖包及其精确的版本号,以便在新环境中进行部署操作。 在虚拟环境使用以下命令将当前虚拟环境中的依赖包以版本号生成至文件中: $ pip freeze >requirements.txt 安装或升级包后,最好更新这个文件以保证虚拟环境中的依赖包。 需求文件的内容示例如下: back

  • 一.Flask基础 1.Flask简介 Flask 本身相当于一个内核,其他几乎所有的功能都要用到扩展(邮件扩展Flask-Mail,用户认证Flask-Login,数据库Flask-SQLAlchemy),都需要用第三方的扩展来实现。比如可以用 Flask 扩展加入ORM、窗体验证工具,文件上传、身份验证等。Flask 没有默认使用的数据库,你可以选择 MySQL,也可以用 NoSQL。 2.r

  • jenkins flask Coverage with Flask 烧瓶覆盖 问题介绍 (Problem introduction) Recently I have started working on a flask project. One of the challenges I have faced was to execute CICD test cases. I have used fl

 相关资料
  • pytest-flask An extension of pytest test runner whichprovides a set of useful tools to simplify testing and developmentof the Flask extensions and applications. To view a more detailed list of extensi

  • pytest 是一个功能齐全的 Python 测试工具,可以帮助编写更好的程序,不仅可以编写小测试,还可以扩展到复杂的功能测试。 特性: 有关失败的断言语句的详细信息(无需记住 self.assert* names) 自动发现测试模块和功能 模块化式具,用于管理小型或参数化的长期测试资源 可以开箱即用运行单元测试、Nose 测试套件 Python 3.5+ 与 PyPy3; 丰富的插件架构,拥有

  • pytest_mozwebqa 是一个插件,能够为 py.test 提供 Mozilla 的 WebQA 项目所需的附加功能。 使用条件: py.test selenium requests  

  • Testinfra test your infrastructure Latest documentation: https://testinfra.readthedocs.io/en/latest About With Testinfra you can write unit tests in Python to test actual state ofyour servers configur

  • 我已经在DebianLinux下安装了pytest 2.3.4。默认情况下,它在Python 2.7下运行,但有时我想在Python 3. x下运行它,它也已安装。我似乎找不到任何关于如何做到这一点的说明。 PyPI Trove分类器显示Python:: 3,所以大概是可能的。除了

  • 面对使用覆盖率运行pytest时出现的问题,我已经浏览了SO帖子,但无法解决此问题,我相信我在这里遗漏了一些东西。。 获取以下错误,其中用户是我项目的应用程序 我的测试。ini文件内容 [pytest]DJANGO_设置_模块=cloudstack。设置 python\u文件=测试。py测试*。py*\u测试。py addopts=-v--ignore=venv--cov=--cov报告=html