user1908678
user1908678

Reputation:

Using different database with pytest

I am using to run tests for my django application pytest with pytest-django plugin. I use MySQL database as a default one for django. Is there possiblity to specify just for pytest to use different database for tests, SQLite?

I am looking for different option if any, than creating special test_seetings.py for py.test --ds=test_settings.

Upvotes: 7

Views: 7005

Answers (2)

Javier Buzzi
Javier Buzzi

Reputation: 6808

If you need to run tests under a "real" database but don't want to use your main database for it because it's too slow, you can use sqlite for the particular test.

import tempfile
from django.db import models, connections
from django.db.backends.sqlite3.base import DatabaseWrapper as SqliteConnection


@pytest.fixture(scope="function")
def sqlite_db(django_db_blocker):
    """
    Runs tests inside a sqlite3, making it slightly faster.
    When using this, avoid using the `@pytest.mark.django_db` and put the name `sqlite_db` as your
    first function parameter.

    Example:

        def test_something(sqlite_db):
            # This test will run inside a unique and isolated sqlite db

            with connection.cursor() as c, connection.schema_editor() as editor:
                editor.create_model(User)  # c.execute('CREATE TABLE ...')
                user = User.objects.create(username='test')  # c.execute('INSERT INTO ...')
                assert user.username == 'test'
                editor.delete_model(User)  # c.execute('DROP TABLE ...')
            
    """
    # Tells pytest-django that it's ok to use the db
    django_db_blocker.unblock()

    # Create a backup of the current db connection
    original_connection = connections['default']

    with tempfile.NamedTemporaryFile() as f:
        db_settings = {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': f.name,
            'TIME_ZONE': None,
            'CONN_MAX_AGE': 0,
            'OPTIONS': {},
            'AUTOCOMMIT': True,
        }
        # Override the `default` connection with the sqlite one
        connections['default'] = SqliteConnection(db_settings)

        # Call your test
        yield

        # Clean up
        connections['default'].close()
        # Put back the connection
        connections['default'] = original_connection

    # Tell pytest-django to restore the db lock
    django_db_blocker.restore()

Note: the lack of @pytest.mark.django_db decorator on the example test (docstring).

Note: this will give you a brand new db with NOTHING inside, you need to create your models and populate it if you want to use it. See editor = connections['default'].schema_editor(); editor.create_model(User) and don't forget to clean up after yourself: editor.delete_model(User)

Upvotes: 0

andreaspelme
andreaspelme

Reputation: 3310

--ds=test_settings is the way to go!

If you dont want to type out --ds=... every time you invoke py.test, you can add a pytest.ini with this contents:

[pytest]
DJANGO_SETTINGS_MODULE = test_settings

As a side note: It is good practice to use the same type of database that you use in production when running your tests. pytest-django provides the --reuse-db option to re-use a MySQL/Postgres database between test runs, saving a lot of setup time when running a few tests for projects with lots of models.

Upvotes: 8

Related Questions