diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-03-03 15:43:46 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-03-03 15:43:46 -0500 |
commit | 15866e58e2066f307ac9e22475c5a61162af4425 (patch) | |
tree | e3f8d49c4736c3795d7c16f9b113dd10dbd26852 | |
parent | 4e8f8d5499067a28f97fd911b6942a24384b67b1 (diff) | |
download | sqlalchemy-15866e58e2066f307ac9e22475c5a61162af4425.tar.gz |
- documentation and changelog
-rw-r--r-- | README.dialects.rst | 67 | ||||
-rw-r--r-- | README.unittests.rst | 173 | ||||
-rw-r--r-- | doc/build/changelog/changelog_09.rst | 30 | ||||
-rwxr-xr-x | sqla_nose.py | 6 | ||||
-rwxr-xr-x | test/conftest.py | 2 |
5 files changed, 199 insertions, 79 deletions
diff --git a/README.dialects.rst b/README.dialects.rst index 26bc1edaf..586e86d29 100644 --- a/README.dialects.rst +++ b/README.dialects.rst @@ -24,6 +24,9 @@ be viewed as the primary target for new dialects, and as it continues to grow and mature it should become a more thorough and efficient system of testing new dialects. +As of SQLAlchemy 0.9.4, both nose and py.test are supported for running tests, +and py.test is now preferred. + Dialect Layout =============== @@ -39,6 +42,7 @@ The file structure of a dialect is typically similar to the following:: <dbapi>.py requirements.py test/ + conftest.py __init__.py test_suite.py test_<dialect_specific_test>.py @@ -66,13 +70,17 @@ Key aspects of this file layout include: create_engine("access+pyodbc://user:pw@dsn") -* setup.cfg - this file contains the traditional contents such as [egg_info] - and [nosetests] directives, but also contains new directives that are used +* setup.cfg - this file contains the traditional contents such as [egg_info], + [pytest] and [nosetests] directives, but also contains new directives that are used by SQLAlchemy's testing framework. E.g. for Access:: [egg_info] tag_build = dev + [pytest] + addopts= --tb native -v -r fxX + python_files=test/*test_*.py + [nosetests] with-sqla_testing = true where = test @@ -89,21 +97,38 @@ Key aspects of this file layout include: sqlite=sqlite:///:memory: Above, the ``[sqla_testing]`` section contains configuration used by - SQLAlchemy's test plugin.The ``[nosetests]`` section includes the - directive ``with-sql_testing = true``, which indicates to Nose that - the SQLAlchemy nose plugin should be used. - -* run_tests.py - The plugin is provided with SQLAlchemy, however is not - plugged into Nose automatically; instead, a ``run_tests.py`` script - should be composed as a front end to Nose, such that SQLAlchemy's plugin - will be correctly installed. - - run_tests.py has two parts. One optional, but probably helpful, step - is that it installs your third party dialect into SQLAlchemy without - using the setuptools entrypoint system; this allows your dialect to - be present without any explicit setup.py step needed. The other - step is to import SQLAlchemy's nose runner and invoke it. An - example run_tests.py file looks like the following:: + SQLAlchemy's test plugin. The ``[pytest]`` and ``[nosetests]`` sections + include directives to help with these runners; in the case of + Nose, the directive ``with-sql_testing = true``, which indicates to Nose that + the SQLAlchemy nose plugin should be used. In the case of py.test, the + test/conftest.py file will bootstrap SQLAlchemy's plugin. + +* test/conftest.py - This script bootstraps SQLAlchemy's py.test plugin + into the py.test runner. This + script can also be used to install your third party dialect into + SQLAlchemy without using the setuptools entrypoint system; this allows + your dialect to be present without any explicit setup.py step needed. + The other portion invokes SQLAlchemy's py.test plugin:: + + from sqlalchemy.dialects import registry + + registry.register("access", "sqlalchemy_access.pyodbc", "AccessDialect_pyodbc") + registry.register("access.pyodbc", "sqlalchemy_access.pyodbc", "AccessDialect_pyodbc") + + from sqlalchemy.testing.plugin.pytestplugin import * + + Where above, the ``registry`` module, introduced in SQLAlchemy 0.8, provides + an in-Python means of installing the dialect entrypoints without the use + of setuptools, using the ``registry.register()`` function in a way that + is similar to the ``entry_points`` directive we placed in our ``setup.py``. + +* run_tests.py - This script is used when running the tests via Nose. + The purpose of the script is to plug in SQLAlchemy's nose plugin into + the Nose environment before the tests run. + + The format of this file is similar to that of conftest.py; first, + the optional but helpful step of registering your third party plugin, + then the other is to import SQLAlchemy's nose runner and invoke it:: from sqlalchemy.dialects import registry @@ -120,10 +145,6 @@ Key aspects of this file layout include: if __name__ == '__main__': runner.main() - Where above, the ``registry`` module, introduced in SQLAlchemy 0.8, provides - an in-Python means of installing the dialect entrypoints without the use - of setuptools, using the ``registry.register()`` function in a way that - is similar to the ``entry_points`` directive we placed in our ``setup.py``. The call to ``runner.main()`` then runs the Nose front end, which installs SQLAlchemy's testing plugins. Invoking our custom runner looks like the following:: @@ -175,12 +196,12 @@ Key aspects of this file layout include: dialect take place:: cd /path/to/sqlalchemy - python ./sqla_nose.py -v \ + py.test -v \ --requirements sqlalchemy_access.requirements:Requirements \ --dburi access+pyodbc://admin@access_test * test_suite.py - Finally, the ``test_suite.py`` module represents a - Nose test suite, which pulls in the actual SQLAlchemy test suite. + stub test suite, which pulls in the actual SQLAlchemy test suite. To pull in the suite as a whole, it can be imported in one step:: # test/test_suite.py diff --git a/README.unittests.rst b/README.unittests.rst index 0bac0bcc5..7af5adbc8 100644 --- a/README.unittests.rst +++ b/README.unittests.rst @@ -2,50 +2,62 @@ SQLALCHEMY UNIT TESTS ===================== +**NOTE:** SQLAlchemy as of 0.9.4 now standardizes on `py.test <http://pytest.org/>`_ +for test running! However, the existing support for Nose **still remains**! +That is, you can now run the tests via py.test or nose. We hope to keep the +suite nose-compatible indefinitely however this might change at some point. + SQLAlchemy unit tests by default run using Python's built-in sqlite3 -module. If running on Python 2.4, pysqlite must be installed. +module. If running on a Python installation that doesn't include this +module, then pysqlite or compatible must be installed. + +Unit tests can be run with py.test or nose: + + py.test: http://pytest.org/ -Unit tests are run using nose. Nose is available at:: + nose: https://pypi.python.org/pypi/nose/ - https://pypi.python.org/pypi/nose/ +The suite includes enhanced support when running with py.test, and py.test +should be preferred. -SQLAlchemy implements a nose plugin that must be present when tests are run. -This plugin is invoked when the test runner script provided with -SQLAlchemy is used. +SQLAlchemy implements plugins for both py.test and nose that must be +present when tests are run. In the case of py.test, this plugin is automatically +used when py.test is run against the SQLAlchemy source tree. However, +for Nose support, a special test runner script must be used. -The test suite as of version 0.8.2 also requires the mock library. While + +The test suite as also requires the mock library. While mock is part of the Python standard library as of 3.3, previous versions will need to have it installed, and is available at:: https://pypi.python.org/pypi/mock -**NOTE:** - the nose plugin is no longer installed by setuptools as of -version 0.7 ! Use "python setup.py test" or "./sqla_nose.py". - RUNNING TESTS VIA SETUP.PY -------------------------- -A plain vanilla run of all tests using sqlite can be run via setup.py: +A plain vanilla run of all tests using sqlite can be run via setup.py, and +requires that py.test is installed:: $ python setup.py test -The -v flag also works here:: - $ python setup.py test -v - -RUNNING ALL TESTS ------------------- +RUNNING ALL TESTS - PYTEST +-------------------------- To run all tests:: - $ ./sqla_nose.py + $ py.test + +The py.test configuration in setup.cfg will point the runner at the +test/ directory, where it consumes a conftest.py file that gets everything +else up and running. -If you're running the tests on Microsoft Windows, then there is an additional -argument that must be passed to ./sqla_nose.py:: - > ./sqla_nose.py --first-package-wins +RUNNING ALL TESTS - NOSE +-------------------------- + +When using Nose, a bootstrap script is provided which sets up sys.path +as well as installs the nose plugin:: -This is required because nose's importer will normally evict a package from -sys.modules if it sees a package with the same name in a different location. -Setting this argument disables that behavior. + $ ./sqla_nose.py Assuming all tests pass, this is a very unexciting output. To make it more interesting:: @@ -53,24 +65,43 @@ interesting:: $ ./sqla_nose.py -v RUNNING INDIVIDUAL TESTS -------------------------- +--------------------------------- + Any directory of test modules can be run at once by specifying the directory -path:: +path, and a specific file can be specified as well:: + + $ py.test test/dialect + + $ py.test test/orm/test_mapper.py + +When using nose, the setup.cfg currently sets "where" to "test/", so the +"test/" prefix is omitted:: + + $ ./sqla_nose.py dialect/ - $ ./sqla_nose.py test/dialect + $ ./sqla_nose.py orm/test_mapper.py -Any test module can be run directly by specifying its module name:: +With Nose, it is often more intuitive to specify tests as module paths:: $ ./sqla_nose.py test.orm.test_mapper -To run a specific test within the module, specify it as module:ClassName.methodname:: +Nose can also specify a test class and optional method using this syntax:: $ ./sqla_nose.py test.orm.test_mapper:MapperTest.test_utils +With py.test, the -k flag is used to limit tests:: + + $ py.test test/orm/test_mapper.py -k "MapperTest and test_utils" + COMMAND LINE OPTIONS -------------------- -Help is available via --help:: + +SQLAlchemy-specific options are added to both runners, which are viewable +within the help screen. With py.test, these options are easier to locate +as they are underneath the "sqlalchemy" grouping:: + + $ py.test --help $ ./sqla_nose.py --help @@ -78,6 +109,9 @@ The --help screen is a combination of common nose options and options which the SQLAlchemy nose plugin adds. The most commonly SQLAlchemy-specific options used are '--db' and '--dburi'. +Both py.test and nose support the same set of SQLAlchemy options, though +py.test features a bit more capability with them. + DATABASE TARGETS ---------------- @@ -87,6 +121,63 @@ another database, use the --dburi option with any standard SQLAlchemy URL:: --dburi=postgresql://user:password@localhost/test +If you'll be running the tests frequently, database aliases can save a lot of +typing. The --dbs option lists the built-in aliases and their matching URLs:: + + $ py.test --dbs + Available --db options (use --dburi to override) + mysql mysql://scott:tiger@127.0.0.1:3306/test + oracle oracle://scott:tiger@127.0.0.1:1521 + postgresql postgresql://scott:tiger@127.0.0.1:5432/test + [...] + +To run tests against an aliased database:: + + $ py.test --db postgresql + +This list of database urls is present in the setup.cfg file. The list +can be modified/extended by adding a file ``test.cfg`` at the +top level of the SQLAlchemy source distribution which includes +additional entries:: + + [db] + postgresql=postgresql://myuser:mypass@localhost/mydb + +Your custom entries will override the defaults and you'll see them reflected +in the output of --dbs. + +MULTIPLE DATABASE TARGETS +------------------------- + +As of SQLAlchemy 0.9.4, the test runner supports **multiple databases at once**. +This doesn't mean that the entire test suite runs for each database, but +instead specific test suites may do so, while other tests may choose to +run on a specific target out of those available. For example, if the tests underneath +test/dialect/ are run, the majority of these tests are either specific to +a particular backend, or are marked as "multiple", meaning they will run repeatedly +for each database in use. If one runs the test suite as follows:: + + $ py.test test/dialect --db sqlite --db postgresql --db mysql + +The tests underneath test/dialect/test_suite.py will be tripled up, running +as appropriate for each target database, whereas dialect-specific tests +within test/dialect/mysql, test/dialect/postgresql/ test/dialect/test_sqlite.py +should run fully with no skips, as each suite has its target database available. + +The multiple targets feature is available both under py.test and nose, +however when running nose, the "multiple runner" feature won't be available; +instead, the first database target will be used. + +When running with multiple targets, tests that don't prefer a specific target +will be run against the first target specified. Putting sqlite first in +the list will lead to a much faster suite as the in-memory database is +extremely fast for setting up and tearing down tables. + + + +DATABASE CONFIGURATION +---------------------- + Use an empty database and a database user with general DBA privileges. The test suite will be creating and dropping many tables and other DDL, and preexisting tables will interfere with the tests. @@ -160,30 +251,6 @@ Additional steps specific to individual databases are as follows:: requires using a test.cfg configuration file as the cmd.exe shell won't properly pass the URL arguments into the nose test runner. -If you'll be running the tests frequently, database aliases can save a lot of -typing. The --dbs option lists the built-in aliases and their matching URLs:: - - $ ./sqla_nose.py --dbs - Available --db options (use --dburi to override) - mysql mysql://scott:tiger@127.0.0.1:3306/test - oracle oracle://scott:tiger@127.0.0.1:1521 - postgresql postgresql://scott:tiger@127.0.0.1:5432/test - [...] - -To run tests against an aliased database:: - - $ ./sqla_nose.py --db=postgresql - -To customize the URLs with your own users or hostnames, create a file -called `test.cfg` at the top level of the SQLAlchemy source distribution. -This file is in Python config format, and contains a [db] section which -lists out additional database configurations:: - - [db] - postgresql=postgresql://myuser:mypass@localhost/mydb - -Your custom entries will override the defaults and you'll see them reflected -in the output of --dbs. CONFIGURING LOGGING ------------------- diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst index 5f01883b0..ad74cf9f7 100644 --- a/doc/build/changelog/changelog_09.rst +++ b/doc/build/changelog/changelog_09.rst @@ -15,7 +15,35 @@ :version: 0.9.4 .. change:: - :tags: orm feature + :tags: feature, general + + Support has been added for py.test to run tests. This runner + is currently being supported in addition to nose, and will likely + be preferred to nose going forward. The nose plugin system used + by SQLAlchemy has been split out so that it works under py.test as + well. There are no plans to drop support for nose at the moment + and we hope that the test suite itself can continue to remain as + agnostic of testing platform as possible. See the file + README.unittests.rst for updated information on running tests + with py.test. + + The test plugin system has also been enhanced to support running + tests against mutiple database URLs at once, by specifying the ``--db`` + and/or ``--dburi`` flags multiple times. This does not run the entire test + suite for each database, but instead allows test cases that are specific + to certain backends make use of that backend as the test is run. + When using py.test as the test runner, the system will also run + specific test suites multiple times, once for each database, particularly + those tests within the "dialect suite". The plan is that the enhanced + system will also be used by Alembic, and allow Alembic to run + migration operation tests against multiple backends in one run, including + third-party backends not included within Alembic itself. + Third party dialects and extensions are also encouraged to standardize + on SQLAlchemy's test suite as a basis; see the file README.dialects.rst + for background on building out from SQLAlchemy's test platform. + + .. change:: + :tags: orm, feature :tickets: 2976 Added a new option to :paramref:`.relationship.innerjoin` which is diff --git a/sqla_nose.py b/sqla_nose.py index 3e3540c41..8495ff107 100755 --- a/sqla_nose.py +++ b/sqla_nose.py @@ -9,8 +9,12 @@ installs SQLAlchemy's testing plugin into the local environment. import sys import imp import nose +import warnings - +warnings.warn( + "SQLAlchemy now includes py.test support which is expected to " + "supersede that of nose. Please see README.unittests.rst for updated " + "testing information!") from os import path for pth in ['./lib']: sys.path.insert(0, path.join(path.dirname(path.abspath(__file__)), pth)) diff --git a/test/conftest.py b/test/conftest.py index 8565ac61e..c07e9e25f 100755 --- a/test/conftest.py +++ b/test/conftest.py @@ -10,7 +10,7 @@ import sys import imp from os import path -for pth in ['./lib']: +for pth in ['../lib']: sys.path.insert(0, path.join(path.dirname(path.abspath(__file__)), pth)) from sqlalchemy.testing.plugin.pytestplugin import * |