UnitTest support

Tortoise ORM includes its own helper utilities to assist in unit tests.

Usage

from tortoise.contrib import test

class TestSomething(test.TestCase):
    def test_something(self):
        ...

    async def test_something_async(self):
        ...

    @test.skip('Skip this')
    def test_skip(self):
        ...

    @test.expectedFailure
    def test_something(self):
        ...

To get test.TestCase to work as expected, you need to configure your test environment setup and teardown to call the following:

from tortoise.contrib.test import initializer, finalizer

# In setup
initializer(['module.a', 'module.b.c'])
# With optional db_url, app_label and loop parameters
initializer(['module.a', 'module.b.c'], db_url='...', app_label="someapp", loop=loop)
# Or env-var driven → See Green test runner section below.
env_initializer()

# In teardown
finalizer()

On the DB_URL it should follow the following standard:

TORTOISE_TEST_DB=sqlite:///tmp/test-{}.sqlite TORTOISE_TEST_DB=postgres://postgres:@127.0.0.1:5432/test_{}

The {} is a string-replacement parameter, that will create a randomized database name. This is currently required for test.IsolatedTestCase to function. If you don’t use test.IsolatedTestCase then you can give an absolute address. The SQLite in-memory :memory: database will always work, and is the default.

Test Runners

Green

In your .green file:

initializer = tortoise.contrib.test.env_initializer
finalizer = tortoise.contrib.test.finalizer

And then define the TORTOISE_TEST_MODULES environment variable with a comma separated list of module paths.

Furthermore, you may set the database configuration parameter as an environment variable (defaults to sqlite://:memory:):

TORTOISE_TEST_DB=sqlite:///tmp/test-{}.sqlite TORTOISE_TEST_DB=postgres://postgres:@127.0.0.1:5432/test_{}

Py.test

Note

pytest 5.4.0 & 5.4.1 has a bug that stops it from working with async test cases. You may have to install pytest>=5.4.2 to get it to work.

Run the initializer and finalizer in your conftest.py file:

import os
import pytest
from tortoise.contrib.test import finalizer, initializer

@pytest.fixture(scope="session", autouse=True)
def initialize_tests(request):
    db_url = os.environ.get("TORTOISE_TEST_DB", "sqlite://:memory:")
    initializer(["tests.testmodels"], db_url=db_url, app_label="models")
    request.addfinalizer(finalizer)

Nose2

Load the plugin tortoise.contrib.test.nose2 either via command line:

nose2 --plugin tortoise.contrib.test.nose2 --db-module tortoise.tests.testmodels

Or via the config file:

[unittest]
plugins = tortoise.contrib.test.nose2

[tortoise]
# Must specify at least one module path
db-module =
    tests.testmodels
# You can optionally override the db_url here
db-url = sqlite://testdb-{}.sqlite

Reference

class tortoise.contrib.test.IsolatedTestCase(methodName='runTest')[source]

Bases: SimpleTestCase

An asyncio capable test class that will ensure that an isolated test db is available for each test.

Use this if your test needs perfect isolation.

Note to use {} as a string-replacement parameter, for your DB_URL. That will create a randomised database name.

It will create and destroy a new DB instance for every test. This is obviously slow, but guarantees a fresh DB.

If you define a tortoise_test_modules list, it overrides the DB setup module for the tests.

tortoise_test_modules : Iterable[str | module] = []
class tortoise.contrib.test.SimpleTestCase(methodName='runTest')[source]

Bases: IsolatedAsyncioTestCase

The Tortoise base test class.

This will ensure that your DB environment has a test double set up for use.

An asyncio capable test class that provides some helper functions.

Will run any test_*() function either as sync or async, depending on the signature of the function. If you specify async test_*() then it will run it in an event loop.

Based on asynctest

assertListSortEqual(list1, list2, msg=Ellipsis, sorted_key=None)[source]
Return type:

None

async asyncSetUp()[source]
Return type:

None

async asyncTearDown()[source]
Return type:

None

exception tortoise.contrib.test.SkipTest[source]

Bases: Exception

Raise this exception in a test to skip it.

Usually you can use TestCase.skipTest() or one of the skipping decorators instead of raising this directly.

class tortoise.contrib.test.TestCase(methodName='runTest')[source]

Bases: TruncationTestCase

An asyncio capable test class that will ensure that each test will be run at separate transaction that will rollback on finish.

This is a fast test runner. Don’t use it if your test uses transactions.

async asyncSetUp()[source]
Return type:

None

async asyncTearDown()[source]
Return type:

None

class tortoise.contrib.test.TruncationTestCase(methodName='runTest')[source]

Bases: SimpleTestCase

An asyncio capable test class that will truncate the tables after a test.

Use this when your tests contain transactions.

This is slower than TestCase but faster than IsolatedTestCase. Note that usage of this does not guarantee that auto-number-pks will be reset to 1.

tortoise.contrib.test.env_initializer()[source]

Calls initializer() with parameters mapped from environment variables.

TORTOISE_TEST_MODULES:

A comma-separated list of modules to include (required)

TORTOISE_TEST_APP:

The name of the APP to initialise the modules in (optional)

If not provided, it will default to “models”.

TORTOISE_TEST_DB:

The db_url of the test db. (optional)

If not provided, it will default to an in-memory SQLite DB.

Return type:

None

tortoise.contrib.test.expectedFailure(test_item)[source]

Mark test as expecting failure.

On success it will be marked as unexpected success.

tortoise.contrib.test.finalizer()[source]

Cleans up the DB after testing. Must be called as part of the test environment teardown.

Return type:

None

tortoise.contrib.test.getDBConfig(app_label, modules)[source]

DB Config factory, for use in testing.

Parameters:
app_label

Label of the app (must be distinct for multiple apps).

modules

List of modules to look for models in.

Return type:

dict

tortoise.contrib.test.init_memory_sqlite(models: str | list[str] | None = None) Callable[[...], Callable[[P], Coroutine[None, None, T]]][source]
tortoise.contrib.test.init_memory_sqlite(models: Callable[[P], Coroutine[None, None, T]]) Callable[[P], Coroutine[None, None, T]]

For single file style to run code with memory sqlite

Parameters:
models: str | list[str] | None = None
models: Callable[[P], Coroutine[None, None, T]]

list_of_modules that should be discovered for models, default to [‘__main__’].

Usage:

from tortoise import fields, models, run_async
from tortoise.contrib.test import init_memory_sqlite

class MyModel(models.Model):
    id = fields.IntField(primary_key=True)
    name = fields.TextField()

@init_memory_sqlite
async def run():
    obj = await MyModel.create(name='')
    assert obj.id == 1

if __name__ == '__main__'
    run_async(run)

Custom models example:

@init_memory_sqlite(models=['app.models', 'aerich.models'])
async def run():
    ...
Return type:

Union[Callable[[~P], Coroutine[None, None, ~T]], Callable[…, Callable[[~P], Coroutine[None, None, ~T]]]]

tortoise.contrib.test.initializer(modules, db_url=None, app_label='models', loop=None)[source]

Sets up the DB for testing. Must be called as part of test environment setup.

Parameters:
modules

List of modules to look for models in.

db_url=None

The db_url, defaults to sqlite://:memory.

app_label='models'

The name of the APP to initialise the modules in, defaults to “models”

loop=None

Optional event loop.

Return type:

None

tortoise.contrib.test.requireCapability(connection_name='models', **conditions)[source]

Skip a test if the required capabilities are not matched.

Note

The database must be initialized before the decorated test runs.

Usage:

@requireCapability(dialect='sqlite')
async def test_run_sqlite_only(self):
    ...

Or to conditionally skip a class:

@requireCapability(dialect='sqlite')
class TestSqlite(test.TestCase):
    ...
Parameters:
connection_name='models'

name of the connection to retrieve capabilities from.

**conditions

capability tests which must all pass for the test to run.

Return type:

Callable

tortoise.contrib.test.skip(reason)[source]

Unconditionally skip a test.

tortoise.contrib.test.skipIf(condition, reason)[source]

Skip a test if the condition is true.

tortoise.contrib.test.skipUnless(condition, reason)[source]

Skip a test unless the condition is true.