7

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.

2 Answers2

8

--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.

Two-Bit Alchemist
  • 17,966
  • 6
  • 47
  • 82
andreaspelme
  • 3,270
  • 23
  • 12
  • Running test on a production database? Do you mean a database reprenting a production like database, or a database which is used in production? I hope not the latter. – OrangeTux Oct 15 '13 at 06:29
  • 1
    @OrangeTux I definitely agree with you, that sentence is misleading and not what I intended it to be. To clarify, what I meant is that if you use PostgreSQL in production, you should run your test suite against a PostgreSQL database. You should *not* run your tests agains your actual production database with production data. Some people suggest to use SQLite for local development to speed up tests - I think that is an anti-pattern. --reuse-db is the way to go to still have fast tests *and* make sure your system works with the same kind of database you use in production. – andreaspelme Feb 25 '16 at 10:30
  • (I can't seem to edit the original answer, if anyone with enough reputation can do that, feel free to rephrase the answer) – andreaspelme Feb 25 '16 at 10:31
0

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)

Javier Buzzi
  • 6,296
  • 36
  • 50