Skip to content

Commit ba1fd9f

Browse files
committed
Purge module_engine from codebase + docs
1 parent 73a1205 commit ba1fd9f

File tree

6 files changed

+38
-178
lines changed

6 files changed

+38
-178
lines changed

README.md

Lines changed: 36 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,12 @@ transactions using Flask and SQLAlchemy.
1616
- [Configuration](#configuration)
1717
- [Conftest setup](#conftest-setup)
1818
- [Test configuration (.ini or .cfg file)](#test-configuration-ini-or-cfg-file)
19-
- [`db-connection-string`](#db-connection-string)
2019
- [`mocked-engines`](#mocked-engines)
2120
- [`mocked-sessions`](#mocked-sessions)
2221
- [`mocked-sessionmakers`](#mocked-sessionmakers)
2322
- [Fixtures](#fixtures)
2423
- [`db_session`](#db_session)
2524
- [`db_engine`](#db_engine)
26-
- [`module_engine`](#module_engine)
2725
- [Using the `transactional` mark](#using-the-transactional-mark)
2826
- [**Development**](#development)
2927
- [Running the tests](#running-the-tests)
@@ -250,32 +248,38 @@ def _db(database):
250248

251249
### Test configuration (.ini or .cfg file)
252250

253-
This plugin requires that you set up a test configuration file with a few
254-
specific properties under the `[pytest]` section. For basic background on pytest
255-
configuration, see the [pytest docs](https://docs.pytest.org/en/latest/customize.html#adding-default-options).
251+
This plugin allows you to configure a few different properties in the test
252+
configuration file in order to handle the specific database connection needs
253+
of an app. For basic background on setting up pytest configuration files, see
254+
the [pytest docs](https://docs.pytest.org/en/latest/customize.html#adding-default-options).
256255

257-
#### `db-connection-string`
256+
All three configuration properties ([`mocked-engines`](#mocked-engines),
257+
[`mocked-sessions`](#mocked-sessions), and [`mocked-sessionmakers`](#mocked-sessionmakers))
258+
work by **[patching](https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch)
259+
one or more specified objects during a test**, replacing them with equivalent objects whose
260+
database interactions will run inside of a transaction and ultimately be
261+
rolled back when the test exits. Using these patches, you can call methods from
262+
your codebase that alter database state with the knowledge that no changes will persist
263+
beyond the body of the test.
258264

259-
The `db-connection-string` property allows the plugin to access a test
260-
database. **This property is required.**
265+
The configured patches are applied in tests where either one of two conditions
266+
are true:
261267

262-
Example:
263-
264-
```ini
265-
[pytest]
266-
db-connection-string=postgresql://postgres@localhost:5432/pytest_test
267-
```
268+
1. a transactional fixture ([`db_session`](#db_session) or [`db_engine`](#db_engine))
269+
is listed as a dependency, or
270+
2. the [`@pytest.mark.transactional`](#using-the-transactional-mark)
271+
mark is active.
268272

269273
#### `mocked-engines`
270274

271275
The `mocked-engines` property directs the plugin to [patch](https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch)
272276
objects in your codebase, typically SQLAlchemy [Engine](http://docs.sqlalchemy.org/en/latest/core/connections.html#sqlalchemy.engine.Engine)
273-
instances, replacing them with the [`db_engine` fixture](#db_engine) in tests where either
274-
of the transactional fixtures ([`db_session`](#db_session) or [`db_engine`](#db_engine))
275-
are listed as dependencies. The values for this property should be formatted as standard
276-
Python import paths, like `api.database.engine`.
277+
instances, replacing them with the [`db_engine` fixture](#db_engine) such that
278+
any database updates performed by the objects get rolled back at the end of
279+
the test.
277280

278-
This property is optional.
281+
The value for this property should be formatted as a whitespace-separated list
282+
of standard Python import paths, like `api.database.engine`. This property is **optional**.
279283

280284
Example:
281285

@@ -295,12 +299,12 @@ mocked-engines=api.database.engine api.database.second_engine
295299

296300
The `mocked-sessions` property directs the plugin to [patch](https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch)
297301
objects in your codebase, typically SQLAlchemy [Session](http://docs.sqlalchemy.org/en/latest/core/connections.html#sqlalchemy.engine.Engine)
298-
instances, replacing them with the [`db_session`](#db_session) fixture in tests where either
299-
of the transactional fixtures ([`db_session`](#db_session) or [`db_engine`](#db_engine)) are
300-
listed as dependencies. Values for this property should be formatted as standard Python import
301-
paths, like `api.database.db.session`.
302+
instances, replacing them with the [`db_session`](#db_session) fixture such that
303+
any database updates performed by the objects get rolled back at the end of
304+
the test.
302305

303-
This property is optional.
306+
The value for this property should be formatted as a whitespace-separated list
307+
of standard Python import paths, like `api.database.db.session`. This property is **optional**.
304308

305309
Example:
306310

@@ -319,13 +323,13 @@ mocked-sessions=api.database.db.session api.database.second_db.session
319323
#### `mocked-sessionmakers`
320324

321325
The `mocked-sessionmakers` property directs the plugin to [patch](https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch)
322-
objects in your codebase, typically SQLAlchemy [sessionmaker](http://docs.sqlalchemy.org/en/latest/orm/session_api.html?highlight=sessionmaker#sqlalchemy.orm.session.sessionmaker)
323-
factories, replacing them with a mocked class that will return the [`db_session`](#db_session) fixture
324-
in tests where either of the transactional fixtures ([`db_session`](#db_session) or [`db_engine`](#db_engine))
325-
are listed as dependencies. Values for this property should be formatted as standard Python import paths,
326-
like `api.database.WorkerSessionmaker`.
326+
objects in your codebase, typically [SQLAlchemy `sessionmaker`
327+
factories](http://docs.sqlalchemy.org/en/latest/orm/session_api.html?highlight=sessionmaker#sqlalchemy.orm.session.sessionmaker),
328+
replacing them with a mocked class that will return the transactional
329+
[`db_session`](#db_session) fixture.
327330

328-
This property is optional.
331+
The value for this property should be formatted as a whitespace-separated list
332+
of standard Python import paths, like `api.database.WorkerSessionmaker`. This property is **optional**.
329333

330334
Example:
331335

@@ -345,9 +349,8 @@ mocked-sessionmakers=api.database.WorkerSessionmaker api.database.SecondWorkerSe
345349

346350
This plugin provides two fixtures for performing database updates inside nested
347351
transactions that get rolled back at the end of a test: [`db_session`](#db_session) and
348-
[`db_engine`](#db_engine). In addition, the plugin provides a fixture for direct database
349-
changes that will not get rolled back: [`module_engine`](#module_engine). This fixture can be
350-
useful for setting up module- or session-scoped state.
352+
[`db_engine`](#db_engine). The fixtures provide similar functionality, but
353+
with different APIs.
351354

352355
### `db_session`
353356

@@ -409,30 +412,6 @@ def test_transaction_doesnt_persist(db_engine):
409412
assert row_name != 'testing'
410413
```
411414

412-
### `module_engine`
413-
414-
In contrast to [`db_session`](#db_session) and [`db_engine`](#db_engine),
415-
the `module_engine` fixture does not wrap its test in a database transaction.
416-
Instead, this fixture returns a SQLAlchemy `Engine` object that can be used to
417-
set up persistent state in tests or fixtures. Its API is identical to the
418-
[SQLAlchemy `Engine` API](http://docs.sqlalchemy.org/en/latest/core/connections.html#sqlalchemy.engine.Engine).
419-
420-
Listing this fixture as a dependency **will not** activate any mocks that are specified
421-
by the configuration properties [`mocked-engines`](#mocked-engines), [`mocked-sessions`](#mocked-sessions),
422-
or [`mocked-sessionmakers`](#mocked-sessionmakers) in a configuration file.
423-
424-
Example:
425-
426-
```python
427-
def test_module_engine(module_engine):
428-
with module_engine.begin() as conn:
429-
row = conn.execute('''UPDATE table SET name = 'testing' WHERE id = 1''')
430-
431-
def test_module_engine_changes_persist(db_engine):
432-
row_name = db_engine.execute('''SELECT name FROM table WHERE id = 1''').fetchone()[0]
433-
assert row_name == 'testing'
434-
```
435-
436415
## Using the `transactional` mark
437416

438417
If you want to enforce transactional context but you don't need to use either

tests/test_configs.py

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,3 @@
1-
def test_missing_db_connection_string(db_testdir):
2-
'''
3-
Test the case where the user has forgotten to specify a DB connection string
4-
when attempting to use the `module_engine` fixture.
5-
'''
6-
db_testdir.makepyfile("""
7-
def test_missing_db_connection_string(module_engine):
8-
# The fixture setup should fail, so don't worry about the test
9-
pass
10-
""")
11-
12-
result = db_testdir.runpytest()
13-
result.assert_outcomes(error=1)
14-
result.stdout.fnmatch_lines([
15-
"*The configuration option 'db-connection-string' is required to use the `module_engine` fixture.*",
16-
])
17-
18-
19-
def test_malformed_db_connection_string(db_testdir):
20-
'''
21-
Test the case where a user has provided a DB connection string that does not
22-
produce a valid database connection when attempting to use the `module_engine`
23-
fixture.
24-
'''
25-
db_testdir.makeini("""
26-
[pytest]
27-
db-connection-string=blahblahblah
28-
""")
29-
30-
db_testdir.makepyfile("""
31-
def test_missing_db_connection_string(module_engine):
32-
# The fixture setup should fail, so don't worry about the test
33-
pass
34-
""")
35-
36-
result = db_testdir.runpytest()
37-
result.assert_outcomes(error=1)
38-
result.stdout.fnmatch_lines([
39-
"*SQLAlchemy could not parse a rfc1738 URL from the string*",
40-
])
41-
42-
431
def test_mocked_engines(db_testdir):
442
'''
453
Test that we can specify paths to specific Engine objects that the plugin

tests/test_fixtures.py

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -294,39 +294,3 @@ def test_raw_connection_changes_dont_persist(person, db_engine, db_session):
294294

295295
result = db_testdir.runpytest()
296296
result.assert_outcomes(passed=2)
297-
298-
299-
def test_module_engine(db_testdir):
300-
'''
301-
Make sure that the `module_engine` fixture can produce state changes that
302-
persist across tests.
303-
'''
304-
db_conn = os.environ['TEST_DATABASE_URL']
305-
306-
db_testdir.makeini("""
307-
[pytest]
308-
db-connection-string={}
309-
""".format(db_conn))
310-
311-
db_testdir.makepyfile("""
312-
def test_module_engine(person, module_engine):
313-
314-
module_engine.execute('''
315-
insert into person (id, name)
316-
values (1, 'tester')
317-
''')
318-
319-
new_person = module_engine.execute('''select name from person where id = 1''').fetchone()[0]
320-
assert new_person == 'tester'
321-
322-
def test_module_engine_changes_persist(person, module_engine):
323-
324-
new_person = module_engine.execute('''select name from person where id = 1''').fetchone()[0]
325-
assert new_person == 'tester'
326-
327-
# Perform cleanup
328-
module_engine.execute('''truncate person''')
329-
""")
330-
331-
result = db_testdir.runpytest()
332-
result.assert_outcomes(passed=2)

transactions/exceptions.py

Lines changed: 0 additions & 4 deletions
This file was deleted.

transactions/fixtures.py

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
import pytest
55
import sqlalchemy as sa
66

7-
from .exceptions import ConfigError
8-
97

108
@pytest.fixture(scope='function')
119
def _transaction(request, _db, mocker):
@@ -183,34 +181,3 @@ def db_engine(_engine, _session, _transaction):
183181
SQLAlchemy Engine API, just as you might use the `api.database.engine` object.
184182
'''
185183
return _engine
186-
187-
188-
@pytest.fixture(scope='module')
189-
def module_engine(pytestconfig, request):
190-
'''
191-
A module-scoped Engine object for use in setting up fixture state.
192-
193-
This fixture is useful for setting up module-scoped fixtures, but it should
194-
be avoided wherever possible in tests, since it does not enforce transactional
195-
context.
196-
'''
197-
# Make sure that the user has passed in a connection string for the database
198-
if pytestconfig._dbconn == '':
199-
raise ConfigError("The configuration option 'db-connection-string' is required " +
200-
'to use the `module_engine` fixture. Check your pytest config ' +
201-
'file and make sure that this option is specified correctly.')
202-
203-
try:
204-
engine = sa.create_engine(pytestconfig._dbconn)
205-
except sa.exc.ArgumentError:
206-
raise ConfigError("SQLAlchemy could not parse a rfc1738 URL from the string " +
207-
"'%s', defined in the 'db-connection-string' variable " % pytestconfig._dbconn +
208-
"in your .ini file. For help defining a valid connection string, " +
209-
"see the SQLAlchemy docs for the 'create_engine' method: " +
210-
"http://docs.sqlalchemy.org/en/latest/core/engines.html#sqlalchemy.create_engine")
211-
212-
@request.addfinalizer
213-
def dispose():
214-
engine.dispose()
215-
216-
return engine

transactions/plugin.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
from .fixtures import _transaction, _engine, _session, db_session, db_engine, module_engine
2-
from .hooks import *
1+
from .fixtures import _transaction, _engine, _session, db_session, db_engine
2+
from .hooks import pytest_collection_modifyitems
33

44
def pytest_addoption(parser):
55
'''
66
Add additional command-line args.
77
'''
8-
parser.addini('db-connection-string',
9-
help='A connection string specifiying the path to your test database.')
10-
118
base_msg = ('A whitespace-separated list of {obj} objects that should ' +
129
'be mocked and replaced with a transactional equivalent. ' +
1310
'Each instance should be formatted as a standard ' +
@@ -31,7 +28,6 @@ def pytest_configure(config):
3128
'''
3229
Add transactional options to pytest's configuration.
3330
'''
34-
config._dbconn = config.getini('db-connection-string')
3531
config._mocked_engines = config.getini('mocked-engines')
3632
config._mocked_sessions = config.getini('mocked-sessions')
3733
config._mocked_sessionmakers = config.getini('mocked-sessionmakers')

0 commit comments

Comments
 (0)