Pytest-django: run migrate if needed...

Created on 17 Nov 2016  路  11Comments  路  Source: pytest-dev/pytest-django

On bigger projects, the test database creation can takes much time.

To optimize this, i use "--nomigrations" and "--reuse-db" ... great, now i can run tests often and it's no time killer...

But if i make a schema migration and add a new migration file, then it breaks. (e.g. on CI server)...

So: Can pytest detect if there are new schema changes, since last run? If so: force "--create-db" or (maybe better) ignore "--nomigrations"

Maybe add something like this:

  • --auto-migrate - Start migration if schema changes, since last run
  • --auto-create-db - Create new DB if schema changes, since last run

IMHO this can obsolete some other issues, like:
https://github.com/pytest-dev/pytest-django/issues/212
https://github.com/pytest-dev/pytest-django/issues/413

Most helpful comment

Agreed, we need a better, safer and faster way to deal with migration changes. Forcing --create-db automatically on migration changes would be really nice.

Here is one way to detect if the migrations changed: Hash all migration files in the project. I use this strategy successfully for CI runs (outside of pytest-django). Here is my script that calculates a hash of the current migrations: https://gist.github.com/pelme/4b3dac475cd6b1dec4fd67d25d2e7cdc

I'd really like this pattern part of pytest-django. It would ease development and speed up CI runs. Hashing the migration files is also waaaaayy faster than running the migration resolver just to figure out that no migrations needs to be applied. The hash could be calculated and stored in a _pytest_django_migration_hash table or something like that.

There are a couple of orthogonal issues here, this is the way I see it:

Should the test database be recreated or reused?

This is pretty much solved already with --reuse-db and --create-db. We should improve this with migration hashing and automatically applying --create-db on migration hash changes (see above).

If the database should be recreated, how should it happen?

We could add a new flag for this, maybe --migrations= with the following options:

  • Avoid migrations altogether and just recreate from models --migrations=disabled (--nomigrations today
  • Re-use existing database and apply migrations: --migrations=incremental (a new option that is not available today but proposed in #413)
  • Recreate from scratch with the current migrations: --migrations=full (todays default when not specifying --nomigrations)

If we are using xdist and an xdist database does not exist, how should slave databases be created?

We could add a flag for this, maybe --xdist-database= ?

  • Create non-xdist-suffixed database (respecting the above --migrations= flag) and copy it: --xdist-database=copy
  • Create each xdist database from scratch respecting --migrations without first creating a template database: --xdist-database=create.

What do you think @jedie @blueyed ?

All 11 comments

Agreed, we need a better, safer and faster way to deal with migration changes. Forcing --create-db automatically on migration changes would be really nice.

Here is one way to detect if the migrations changed: Hash all migration files in the project. I use this strategy successfully for CI runs (outside of pytest-django). Here is my script that calculates a hash of the current migrations: https://gist.github.com/pelme/4b3dac475cd6b1dec4fd67d25d2e7cdc

I'd really like this pattern part of pytest-django. It would ease development and speed up CI runs. Hashing the migration files is also waaaaayy faster than running the migration resolver just to figure out that no migrations needs to be applied. The hash could be calculated and stored in a _pytest_django_migration_hash table or something like that.

There are a couple of orthogonal issues here, this is the way I see it:

Should the test database be recreated or reused?

This is pretty much solved already with --reuse-db and --create-db. We should improve this with migration hashing and automatically applying --create-db on migration hash changes (see above).

If the database should be recreated, how should it happen?

We could add a new flag for this, maybe --migrations= with the following options:

  • Avoid migrations altogether and just recreate from models --migrations=disabled (--nomigrations today
  • Re-use existing database and apply migrations: --migrations=incremental (a new option that is not available today but proposed in #413)
  • Recreate from scratch with the current migrations: --migrations=full (todays default when not specifying --nomigrations)

If we are using xdist and an xdist database does not exist, how should slave databases be created?

We could add a flag for this, maybe --xdist-database= ?

  • Create non-xdist-suffixed database (respecting the above --migrations= flag) and copy it: --xdist-database=copy
  • Create each xdist database from scratch respecting --migrations without first creating a template database: --xdist-database=create.

What do you think @jedie @blueyed ?

Sounds reasonable to me, I like the --migrations=incremental part of it.
That's also what happens when using --reuse-db without --no-migrations, no? IIRC Django will just apply all missing migrations then.

Note: pytest -vvv prints info about synchronized or migrated tables.

As @blueyed told --reuse-db without --no-migrations will run new migrations.

I love this feature idea. Maybe it can even use pytest's builtin cache to store the hash @pelme implemented.

@chingc
What would make it better than --reuse-db without --no-migrations?
What is your use case, and what defaults do you use?

@blueyed My default pytest config includes --reuse-db with --migrations, but I do find myself having to use --create-db from time to time.

I am going to close this because as @blueyed says this is just the default Django behavior -- the solution is simply not to use --no-migrations.

The issue would actually make sense about apply Django DB fixtures, but this issue is about migrations.

If I'm missing something please let me know.

Where did @blueyed say it's default Django behaviour?

When you say it's default Django behaviour does that mean that pytest-django's --reuse-db and --keep-db back onto the the Django test flag --keepdb?

My personal experience of this; our migrations take like 30 seconds to run. So per the doco here https://pytest-django.readthedocs.io/en/latest/database.html#example-work-flow-with-reuse-db-and-create-db I have --reuse-db in pytest.ini.
Which is great most of the time but frequently when I switch between branches with migrations or otherwise mess with migrations I will get a complete test suite fail (but slowly and with massive amounts of error output). Then I need to run with --create-db and further that needs to be done separately for both single and multi threaded test runs and I have to remember to take it back off the args for followup runs or I will eat another 30 seconds each time.

It would be very very nice if it realised that the set of migrations in the db and the migrations on the drive are different and did a rebuild. Even just comparing the file names would be a vast improvement.

@tolomea The original issue as I understand it talks about a regular schema change + migration flow, for which --reuse-db + default django behavior is sufficient. You are talking about a different scenario, in which you switch a branch and now the state of the test database is broken and it needs to be rebuilt, which pytest-django doesn't know about. I think that's pretty interesting, I agree it would be nice to have pytest-django detect this and possibility handle it automatically. Could you open a new issue with your feature request, referencing this one?

Sorry, I misunderstood what this issue was about, I have created #883

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rlskoeser picture rlskoeser  路  7Comments

mpasternak picture mpasternak  路  5Comments

AndreaCrotti picture AndreaCrotti  路  5Comments

aljosa picture aljosa  路  8Comments

koxu1996 picture koxu1996  路  3Comments