diff options
author | CoprDistGit <infra@openeuler.org> | 2023-04-11 01:15:01 +0000 |
---|---|---|
committer | CoprDistGit <infra@openeuler.org> | 2023-04-11 01:15:01 +0000 |
commit | c8172fa2547572f34c79bfd91602ff8efb657413 (patch) | |
tree | 788887ba3df55032e9f5c22773e568a95bdf534c | |
parent | 21322394cb06efe0104f08fc2d0dc3312a504169 (diff) |
automatic import of python-django-test-migrations
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | python-django-test-migrations.spec | 1144 | ||||
-rw-r--r-- | sources | 1 |
3 files changed, 1146 insertions, 0 deletions
@@ -0,0 +1 @@ +/django-test-migrations-1.2.0.tar.gz diff --git a/python-django-test-migrations.spec b/python-django-test-migrations.spec new file mode 100644 index 0000000..c76059c --- /dev/null +++ b/python-django-test-migrations.spec @@ -0,0 +1,1144 @@ +%global _empty_manifest_terminate_build 0 +Name: python-django-test-migrations +Version: 1.2.0 +Release: 1 +Summary: Test django schema and data migrations, including ordering +License: MIT +URL: https://github.com/wemake-services/django-test-migrations +Source0: https://mirrors.nju.edu.cn/pypi/web/packages/52/9c/3c36d2a827497abb82b426b754497beb86b00f4334b5a33eec687a354436/django-test-migrations-1.2.0.tar.gz +BuildArch: noarch + +Requires: python3-typing_extensions + +%description +# django-test-migrations + +[](https://wemake.services) +[](https://github.com/wemake-services/django-test-migrations/actions?query=workflow%3Atest) +[](https://codecov.io/gh/wemake-services/django-test-migrations) +[](https://pypi.org/project/django-test-migrations/) + +[](https://github.com/wemake-services/wemake-python-styleguide) + + +## Features + +- Allows to test `django` schema and data migrations +- Allows to test both forward and rollback migrations +- Allows to test the migrations order +- Allows to test migration names +- Allows to test database configuration +- Fully typed with annotations and checked with `mypy`, [PEP561 compatible](https://www.python.org/dev/peps/pep-0561/) +- Easy to start: has lots of docs, tests, and tutorials + +Read the [announcing post](https://sobolevn.me/2019/10/testing-django-migrations). +See real-world [usage example](https://github.com/wemake-services/wemake-django-template). + + +## Installation + +```bash +pip install django-test-migrations +``` + +We support several `django` versions: + +- `1.11` +- `2.2` +- `3.1` +- `3.2` +- `4.0` + +Other versions might work too, but they are not officially supported. + + +## Testing django migrations + +Testing migrations is not a frequent thing in `django` land. +But, sometimes it is totally required. When? + +When we do complex schema or data changes +and what to be sure that existing data won't be corrupted. +We might also want to be sure that all migrations can be safely rolled back. +And as a final touch we want to be sure that migrations +are in the correct order and have correct dependencies. + +### Testing forward migrations + +To test all migrations we have a [`Migrator`](https://github.com/wemake-services/django-test-migrations/blob/master/django_test_migrations/migrator.py) class. + +It has three methods to work with: + +- `.apply_initial_migration()` which takes app and migration names to generate + a state before the actual migration happens. It creates the `before state` + by applying all migrations up to and including the ones passed as an argument. + +- `.apply_tested_migration()` which takes app and migration names to perform the + actual migration + +- `.reset()` to clean everything up after we are done with testing + +So, here's an example: + +```python +from django_test_migrations.migrator import Migrator + +migrator = Migrator(database='default') + +# Initial migration, currently our model has only a single string field: +# Note: +# We are testing migration `0002_someitem_is_clean`, so we are specifying +# the name of the previous migration (`0001_initial`) in the +# .apply_initial_migration() method in order to prepare a state of the database +# before applying the migration we are going to test. +# +old_state = migrator.apply_initial_migration(('main_app', '0001_initial')) +SomeItem = old_state.apps.get_model('main_app', 'SomeItem') + +# Let's create a model with just a single field specified: +SomeItem.objects.create(string_field='a') +assert len(SomeItem._meta.get_fields()) == 2 # id + string_field + +# Now this migration will add `is_clean` field to the model: +new_state = migrator.apply_tested_migration( + ('main_app', '0002_someitem_is_clean'), +) +SomeItem = new_state.apps.get_model('main_app', 'SomeItem') + +# We can now test how our migration worked, new field is there: +assert SomeItem.objects.filter(is_clean=True).count() == 0 +assert len(SomeItem._meta.get_fields()) == 3 # id + string_field + is_clean + +# Cleanup: +migrator.reset() +``` + +That was an example of a forward migration. + +### Backward migration + +The thing is that you can also test backward migrations. +Nothing really changes except migration names that you pass and your logic: + +```python +migrator = Migrator() + +# Currently our model has two field, but we need a rollback: +old_state = migrator.apply_initial_migration( + ('main_app', '0002_someitem_is_clean'), +) +SomeItem = old_state.apps.get_model('main_app', 'SomeItem') + +# Create some data to illustrate your cases: +# ... + +# Now this migration will drop `is_clean` field: +new_state = migrator.apply_tested_migration(('main_app', '0001_initial')) + +# Assert the results: +# ... + +# Cleanup: +migrator.reset() +``` + +### Testing migrations ordering + +Sometimes we also want to be sure that our migrations are in the correct order. +And all our `dependecies = [...]` are correct. + +To achieve that we have [`plan.py`](https://github.com/wemake-services/django-test-migrations/blob/master/django_test_migrations/plan.py) module. + +That's how it can be used: + +```python +from django_test_migrations.plan import all_migrations, nodes_to_tuples + +main_migrations = all_migrations('default', ['main_app', 'other_app']) +assert nodes_to_tuples(main_migrations) == [ + ('main_app', '0001_initial'), + ('main_app', '0002_someitem_is_clean'), + ('other_app', '0001_initial'), + ('main_app', '0003_update_is_clean'), + ('main_app', '0004_auto_20191119_2125'), + ('other_app', '0002_auto_20191120_2230'), +] +``` + +This way you can be sure that migrations +and apps that depend on each other will be executed in the correct order. + + +## Test framework integrations 🐍 + +We support several test frameworks as first-class citizens. +That's a testing tool after all! + +Note that the Django `post_migrate` signal's receiver list is cleared at +the start of tests and restored afterwards. If you need to test your +own `post_migrate` signals then attach/remove them during a test. + +### pytest + +We ship `django-test-migrations` with a `pytest` plugin +that provides two convinient fixtures: + +- `migrator_factory` that gives you an opportunity + to create `Migrator` classes for any database +- `migrator` instance for the `'default'` database + +That's how it can be used: + +```python +import pytest + +@pytest.mark.django_db() +def test_pytest_plugin_initial(migrator): + """Ensures that the initial migration works.""" + old_state = migrator.apply_initial_migration(('main_app', None)) + + with pytest.raises(LookupError): + # Models does not yet exist: + old_state.apps.get_model('main_app', 'SomeItem') + + new_state = migrator.apply_tested_migration(('main_app', '0001_initial')) + # After the initial migration is done, we can use the model state: + SomeItem = new_state.apps.get_model('main_app', 'SomeItem') + assert SomeItem.objects.filter(string_field='').count() == 0 +``` + +### unittest + +We also ship an integration with the built-in `unittest` framework. + +Here's how it can be used: + +```python +from django_test_migrations.contrib.unittest_case import MigratorTestCase + +class TestDirectMigration(MigratorTestCase): + """This class is used to test direct migrations.""" + + migrate_from = ('main_app', '0002_someitem_is_clean') + migrate_to = ('main_app', '0003_update_is_clean') + + def prepare(self): + """Prepare some data before the migration.""" + SomeItem = self.old_state.apps.get_model('main_app', 'SomeItem') + SomeItem.objects.create(string_field='a') + SomeItem.objects.create(string_field='a b') + + def test_migration_main0003(self): + """Run the test itself.""" + SomeItem = self.new_state.apps.get_model('main_app', 'SomeItem') + + assert SomeItem.objects.count() == 2 + assert SomeItem.objects.filter(is_clean=True).count() == 1 +``` + +### Choosing only migrations tests + +In CI systems it is important to get instant feedback. Running tests that +apply database migration can slow down tests execution, so it is often a good +idea to run standard, fast, regular unit tests without migrations in parallel +with slower migrations tests. + +#### pytest + +`django_test_migrations` adds `migration_test` marker to each test using +`migrator_factory` or `migrator` fixture. +To run only migrations test, use `-m` option: + +```bash +pytest -m migration_test # runs only migraion tests +pytest -m "not migration_test" # runs all except migraion tests +``` + +#### unittest + +`django_test_migrations` adds `migration_test` +[tag](https://docs.djangoproject.com/en/3.0/topics/testing/tools/#tagging-tests) +to every `MigratorTestCase` subclass. +To run only migrations tests, use `--tag` option: + +```bash +python mange.py test --tag=migration_test # runs only migraion tests +python mange.py test --exclude-tag=migration_test # runs all except migraion tests +``` + + +## Django Checks + +`django_test_migrations` comes with 2 groups of Django's checks for: + ++ detecting migrations scripts automatically generated names ++ validating some subset of database settings + +### Testing migration names + +`django` generates migration names for you when you run `makemigrations`. +And these names are bad ([read more](https://adamj.eu/tech/2020/02/24/how-to-disallow-auto-named-django-migrations/) about why it is bad)! +Just look at this: `0004_auto_20191119_2125.py` + +What does this migration do? What changes does it have? + +One can also pass `--name` attribute when creating migrations, but it is easy to forget. + +We offer an automated solution: `django` check +that produces an error for each badly named migration. + +Add our check into your `INSTALLED_APPS`: + +```python +INSTALLED_APPS = [ + # ... + + # Our custom check: + 'django_test_migrations.contrib.django_checks.AutoNames', +] +``` + +And then in your CI run: + +```bash +python manage.py check --deploy +``` + +This way you will be safe from wrong names in your migrations. + +Do you have a migrations that cannot be renamed? Add them to the ignore list: + +```python +# settings.py + +DTM_IGNORED_MIGRATIONS = { + ('main_app', '0004_auto_20191119_2125'), + ('dependency_app', '0001_auto_20201110_2100'), +} +``` + +And we won't complain about them. + +Or you can completely ignore entire app: + +```python +# settings.py + +DTM_IGNORED_MIGRATIONS = { + ('dependency_app', '*'), + ('another_dependency_app', '*'), +} +``` + +### Database configuration + +Add our check to `INSTALLED_APPS`: + +```python +INSTALLED_APPS = [ + # ... + + # Our custom check: + 'django_test_migrations.contrib.django_checks.DatabaseConfiguration', +] +``` + +Then just run `check` management command in your CI like listed in section +above. + + +## Related projects + +You might also like: + +- [django-migration-linter](https://github.com/3YOURMIND/django-migration-linter) - Detect backward incompatible migrations for your django project. +- [wemake-django-template](https://github.com/wemake-services/wemake-django-template/) - Bleeding edge django template focused on code quality and security with both `django-test-migrations` and `django-migration-linter` on board. + + +## Credits + +This project is based on work of other awesome people: + +- [@asfaltboy](https://gist.github.com/asfaltboy/b3e6f9b5d95af8ba2cc46f2ba6eae5e2) +- [@blueyed](https://gist.github.com/blueyed/4fb0a807104551f103e6) +- [@fernandogrd](https://gist.github.com/blueyed/4fb0a807104551f103e6#gistcomment-1546191) +- [@adamchainz](https://adamj.eu/tech/2020/02/24/how-to-disallow-auto-named-django-migrations/) + + +## License + +MIT. + + +%package -n python3-django-test-migrations +Summary: Test django schema and data migrations, including ordering +Provides: python-django-test-migrations +BuildRequires: python3-devel +BuildRequires: python3-setuptools +BuildRequires: python3-pip +%description -n python3-django-test-migrations +# django-test-migrations + +[](https://wemake.services) +[](https://github.com/wemake-services/django-test-migrations/actions?query=workflow%3Atest) +[](https://codecov.io/gh/wemake-services/django-test-migrations) +[](https://pypi.org/project/django-test-migrations/) + +[](https://github.com/wemake-services/wemake-python-styleguide) + + +## Features + +- Allows to test `django` schema and data migrations +- Allows to test both forward and rollback migrations +- Allows to test the migrations order +- Allows to test migration names +- Allows to test database configuration +- Fully typed with annotations and checked with `mypy`, [PEP561 compatible](https://www.python.org/dev/peps/pep-0561/) +- Easy to start: has lots of docs, tests, and tutorials + +Read the [announcing post](https://sobolevn.me/2019/10/testing-django-migrations). +See real-world [usage example](https://github.com/wemake-services/wemake-django-template). + + +## Installation + +```bash +pip install django-test-migrations +``` + +We support several `django` versions: + +- `1.11` +- `2.2` +- `3.1` +- `3.2` +- `4.0` + +Other versions might work too, but they are not officially supported. + + +## Testing django migrations + +Testing migrations is not a frequent thing in `django` land. +But, sometimes it is totally required. When? + +When we do complex schema or data changes +and what to be sure that existing data won't be corrupted. +We might also want to be sure that all migrations can be safely rolled back. +And as a final touch we want to be sure that migrations +are in the correct order and have correct dependencies. + +### Testing forward migrations + +To test all migrations we have a [`Migrator`](https://github.com/wemake-services/django-test-migrations/blob/master/django_test_migrations/migrator.py) class. + +It has three methods to work with: + +- `.apply_initial_migration()` which takes app and migration names to generate + a state before the actual migration happens. It creates the `before state` + by applying all migrations up to and including the ones passed as an argument. + +- `.apply_tested_migration()` which takes app and migration names to perform the + actual migration + +- `.reset()` to clean everything up after we are done with testing + +So, here's an example: + +```python +from django_test_migrations.migrator import Migrator + +migrator = Migrator(database='default') + +# Initial migration, currently our model has only a single string field: +# Note: +# We are testing migration `0002_someitem_is_clean`, so we are specifying +# the name of the previous migration (`0001_initial`) in the +# .apply_initial_migration() method in order to prepare a state of the database +# before applying the migration we are going to test. +# +old_state = migrator.apply_initial_migration(('main_app', '0001_initial')) +SomeItem = old_state.apps.get_model('main_app', 'SomeItem') + +# Let's create a model with just a single field specified: +SomeItem.objects.create(string_field='a') +assert len(SomeItem._meta.get_fields()) == 2 # id + string_field + +# Now this migration will add `is_clean` field to the model: +new_state = migrator.apply_tested_migration( + ('main_app', '0002_someitem_is_clean'), +) +SomeItem = new_state.apps.get_model('main_app', 'SomeItem') + +# We can now test how our migration worked, new field is there: +assert SomeItem.objects.filter(is_clean=True).count() == 0 +assert len(SomeItem._meta.get_fields()) == 3 # id + string_field + is_clean + +# Cleanup: +migrator.reset() +``` + +That was an example of a forward migration. + +### Backward migration + +The thing is that you can also test backward migrations. +Nothing really changes except migration names that you pass and your logic: + +```python +migrator = Migrator() + +# Currently our model has two field, but we need a rollback: +old_state = migrator.apply_initial_migration( + ('main_app', '0002_someitem_is_clean'), +) +SomeItem = old_state.apps.get_model('main_app', 'SomeItem') + +# Create some data to illustrate your cases: +# ... + +# Now this migration will drop `is_clean` field: +new_state = migrator.apply_tested_migration(('main_app', '0001_initial')) + +# Assert the results: +# ... + +# Cleanup: +migrator.reset() +``` + +### Testing migrations ordering + +Sometimes we also want to be sure that our migrations are in the correct order. +And all our `dependecies = [...]` are correct. + +To achieve that we have [`plan.py`](https://github.com/wemake-services/django-test-migrations/blob/master/django_test_migrations/plan.py) module. + +That's how it can be used: + +```python +from django_test_migrations.plan import all_migrations, nodes_to_tuples + +main_migrations = all_migrations('default', ['main_app', 'other_app']) +assert nodes_to_tuples(main_migrations) == [ + ('main_app', '0001_initial'), + ('main_app', '0002_someitem_is_clean'), + ('other_app', '0001_initial'), + ('main_app', '0003_update_is_clean'), + ('main_app', '0004_auto_20191119_2125'), + ('other_app', '0002_auto_20191120_2230'), +] +``` + +This way you can be sure that migrations +and apps that depend on each other will be executed in the correct order. + + +## Test framework integrations 🐍 + +We support several test frameworks as first-class citizens. +That's a testing tool after all! + +Note that the Django `post_migrate` signal's receiver list is cleared at +the start of tests and restored afterwards. If you need to test your +own `post_migrate` signals then attach/remove them during a test. + +### pytest + +We ship `django-test-migrations` with a `pytest` plugin +that provides two convinient fixtures: + +- `migrator_factory` that gives you an opportunity + to create `Migrator` classes for any database +- `migrator` instance for the `'default'` database + +That's how it can be used: + +```python +import pytest + +@pytest.mark.django_db() +def test_pytest_plugin_initial(migrator): + """Ensures that the initial migration works.""" + old_state = migrator.apply_initial_migration(('main_app', None)) + + with pytest.raises(LookupError): + # Models does not yet exist: + old_state.apps.get_model('main_app', 'SomeItem') + + new_state = migrator.apply_tested_migration(('main_app', '0001_initial')) + # After the initial migration is done, we can use the model state: + SomeItem = new_state.apps.get_model('main_app', 'SomeItem') + assert SomeItem.objects.filter(string_field='').count() == 0 +``` + +### unittest + +We also ship an integration with the built-in `unittest` framework. + +Here's how it can be used: + +```python +from django_test_migrations.contrib.unittest_case import MigratorTestCase + +class TestDirectMigration(MigratorTestCase): + """This class is used to test direct migrations.""" + + migrate_from = ('main_app', '0002_someitem_is_clean') + migrate_to = ('main_app', '0003_update_is_clean') + + def prepare(self): + """Prepare some data before the migration.""" + SomeItem = self.old_state.apps.get_model('main_app', 'SomeItem') + SomeItem.objects.create(string_field='a') + SomeItem.objects.create(string_field='a b') + + def test_migration_main0003(self): + """Run the test itself.""" + SomeItem = self.new_state.apps.get_model('main_app', 'SomeItem') + + assert SomeItem.objects.count() == 2 + assert SomeItem.objects.filter(is_clean=True).count() == 1 +``` + +### Choosing only migrations tests + +In CI systems it is important to get instant feedback. Running tests that +apply database migration can slow down tests execution, so it is often a good +idea to run standard, fast, regular unit tests without migrations in parallel +with slower migrations tests. + +#### pytest + +`django_test_migrations` adds `migration_test` marker to each test using +`migrator_factory` or `migrator` fixture. +To run only migrations test, use `-m` option: + +```bash +pytest -m migration_test # runs only migraion tests +pytest -m "not migration_test" # runs all except migraion tests +``` + +#### unittest + +`django_test_migrations` adds `migration_test` +[tag](https://docs.djangoproject.com/en/3.0/topics/testing/tools/#tagging-tests) +to every `MigratorTestCase` subclass. +To run only migrations tests, use `--tag` option: + +```bash +python mange.py test --tag=migration_test # runs only migraion tests +python mange.py test --exclude-tag=migration_test # runs all except migraion tests +``` + + +## Django Checks + +`django_test_migrations` comes with 2 groups of Django's checks for: + ++ detecting migrations scripts automatically generated names ++ validating some subset of database settings + +### Testing migration names + +`django` generates migration names for you when you run `makemigrations`. +And these names are bad ([read more](https://adamj.eu/tech/2020/02/24/how-to-disallow-auto-named-django-migrations/) about why it is bad)! +Just look at this: `0004_auto_20191119_2125.py` + +What does this migration do? What changes does it have? + +One can also pass `--name` attribute when creating migrations, but it is easy to forget. + +We offer an automated solution: `django` check +that produces an error for each badly named migration. + +Add our check into your `INSTALLED_APPS`: + +```python +INSTALLED_APPS = [ + # ... + + # Our custom check: + 'django_test_migrations.contrib.django_checks.AutoNames', +] +``` + +And then in your CI run: + +```bash +python manage.py check --deploy +``` + +This way you will be safe from wrong names in your migrations. + +Do you have a migrations that cannot be renamed? Add them to the ignore list: + +```python +# settings.py + +DTM_IGNORED_MIGRATIONS = { + ('main_app', '0004_auto_20191119_2125'), + ('dependency_app', '0001_auto_20201110_2100'), +} +``` + +And we won't complain about them. + +Or you can completely ignore entire app: + +```python +# settings.py + +DTM_IGNORED_MIGRATIONS = { + ('dependency_app', '*'), + ('another_dependency_app', '*'), +} +``` + +### Database configuration + +Add our check to `INSTALLED_APPS`: + +```python +INSTALLED_APPS = [ + # ... + + # Our custom check: + 'django_test_migrations.contrib.django_checks.DatabaseConfiguration', +] +``` + +Then just run `check` management command in your CI like listed in section +above. + + +## Related projects + +You might also like: + +- [django-migration-linter](https://github.com/3YOURMIND/django-migration-linter) - Detect backward incompatible migrations for your django project. +- [wemake-django-template](https://github.com/wemake-services/wemake-django-template/) - Bleeding edge django template focused on code quality and security with both `django-test-migrations` and `django-migration-linter` on board. + + +## Credits + +This project is based on work of other awesome people: + +- [@asfaltboy](https://gist.github.com/asfaltboy/b3e6f9b5d95af8ba2cc46f2ba6eae5e2) +- [@blueyed](https://gist.github.com/blueyed/4fb0a807104551f103e6) +- [@fernandogrd](https://gist.github.com/blueyed/4fb0a807104551f103e6#gistcomment-1546191) +- [@adamchainz](https://adamj.eu/tech/2020/02/24/how-to-disallow-auto-named-django-migrations/) + + +## License + +MIT. + + +%package help +Summary: Development documents and examples for django-test-migrations +Provides: python3-django-test-migrations-doc +%description help +# django-test-migrations + +[](https://wemake.services) +[](https://github.com/wemake-services/django-test-migrations/actions?query=workflow%3Atest) +[](https://codecov.io/gh/wemake-services/django-test-migrations) +[](https://pypi.org/project/django-test-migrations/) + +[](https://github.com/wemake-services/wemake-python-styleguide) + + +## Features + +- Allows to test `django` schema and data migrations +- Allows to test both forward and rollback migrations +- Allows to test the migrations order +- Allows to test migration names +- Allows to test database configuration +- Fully typed with annotations and checked with `mypy`, [PEP561 compatible](https://www.python.org/dev/peps/pep-0561/) +- Easy to start: has lots of docs, tests, and tutorials + +Read the [announcing post](https://sobolevn.me/2019/10/testing-django-migrations). +See real-world [usage example](https://github.com/wemake-services/wemake-django-template). + + +## Installation + +```bash +pip install django-test-migrations +``` + +We support several `django` versions: + +- `1.11` +- `2.2` +- `3.1` +- `3.2` +- `4.0` + +Other versions might work too, but they are not officially supported. + + +## Testing django migrations + +Testing migrations is not a frequent thing in `django` land. +But, sometimes it is totally required. When? + +When we do complex schema or data changes +and what to be sure that existing data won't be corrupted. +We might also want to be sure that all migrations can be safely rolled back. +And as a final touch we want to be sure that migrations +are in the correct order and have correct dependencies. + +### Testing forward migrations + +To test all migrations we have a [`Migrator`](https://github.com/wemake-services/django-test-migrations/blob/master/django_test_migrations/migrator.py) class. + +It has three methods to work with: + +- `.apply_initial_migration()` which takes app and migration names to generate + a state before the actual migration happens. It creates the `before state` + by applying all migrations up to and including the ones passed as an argument. + +- `.apply_tested_migration()` which takes app and migration names to perform the + actual migration + +- `.reset()` to clean everything up after we are done with testing + +So, here's an example: + +```python +from django_test_migrations.migrator import Migrator + +migrator = Migrator(database='default') + +# Initial migration, currently our model has only a single string field: +# Note: +# We are testing migration `0002_someitem_is_clean`, so we are specifying +# the name of the previous migration (`0001_initial`) in the +# .apply_initial_migration() method in order to prepare a state of the database +# before applying the migration we are going to test. +# +old_state = migrator.apply_initial_migration(('main_app', '0001_initial')) +SomeItem = old_state.apps.get_model('main_app', 'SomeItem') + +# Let's create a model with just a single field specified: +SomeItem.objects.create(string_field='a') +assert len(SomeItem._meta.get_fields()) == 2 # id + string_field + +# Now this migration will add `is_clean` field to the model: +new_state = migrator.apply_tested_migration( + ('main_app', '0002_someitem_is_clean'), +) +SomeItem = new_state.apps.get_model('main_app', 'SomeItem') + +# We can now test how our migration worked, new field is there: +assert SomeItem.objects.filter(is_clean=True).count() == 0 +assert len(SomeItem._meta.get_fields()) == 3 # id + string_field + is_clean + +# Cleanup: +migrator.reset() +``` + +That was an example of a forward migration. + +### Backward migration + +The thing is that you can also test backward migrations. +Nothing really changes except migration names that you pass and your logic: + +```python +migrator = Migrator() + +# Currently our model has two field, but we need a rollback: +old_state = migrator.apply_initial_migration( + ('main_app', '0002_someitem_is_clean'), +) +SomeItem = old_state.apps.get_model('main_app', 'SomeItem') + +# Create some data to illustrate your cases: +# ... + +# Now this migration will drop `is_clean` field: +new_state = migrator.apply_tested_migration(('main_app', '0001_initial')) + +# Assert the results: +# ... + +# Cleanup: +migrator.reset() +``` + +### Testing migrations ordering + +Sometimes we also want to be sure that our migrations are in the correct order. +And all our `dependecies = [...]` are correct. + +To achieve that we have [`plan.py`](https://github.com/wemake-services/django-test-migrations/blob/master/django_test_migrations/plan.py) module. + +That's how it can be used: + +```python +from django_test_migrations.plan import all_migrations, nodes_to_tuples + +main_migrations = all_migrations('default', ['main_app', 'other_app']) +assert nodes_to_tuples(main_migrations) == [ + ('main_app', '0001_initial'), + ('main_app', '0002_someitem_is_clean'), + ('other_app', '0001_initial'), + ('main_app', '0003_update_is_clean'), + ('main_app', '0004_auto_20191119_2125'), + ('other_app', '0002_auto_20191120_2230'), +] +``` + +This way you can be sure that migrations +and apps that depend on each other will be executed in the correct order. + + +## Test framework integrations 🐍 + +We support several test frameworks as first-class citizens. +That's a testing tool after all! + +Note that the Django `post_migrate` signal's receiver list is cleared at +the start of tests and restored afterwards. If you need to test your +own `post_migrate` signals then attach/remove them during a test. + +### pytest + +We ship `django-test-migrations` with a `pytest` plugin +that provides two convinient fixtures: + +- `migrator_factory` that gives you an opportunity + to create `Migrator` classes for any database +- `migrator` instance for the `'default'` database + +That's how it can be used: + +```python +import pytest + +@pytest.mark.django_db() +def test_pytest_plugin_initial(migrator): + """Ensures that the initial migration works.""" + old_state = migrator.apply_initial_migration(('main_app', None)) + + with pytest.raises(LookupError): + # Models does not yet exist: + old_state.apps.get_model('main_app', 'SomeItem') + + new_state = migrator.apply_tested_migration(('main_app', '0001_initial')) + # After the initial migration is done, we can use the model state: + SomeItem = new_state.apps.get_model('main_app', 'SomeItem') + assert SomeItem.objects.filter(string_field='').count() == 0 +``` + +### unittest + +We also ship an integration with the built-in `unittest` framework. + +Here's how it can be used: + +```python +from django_test_migrations.contrib.unittest_case import MigratorTestCase + +class TestDirectMigration(MigratorTestCase): + """This class is used to test direct migrations.""" + + migrate_from = ('main_app', '0002_someitem_is_clean') + migrate_to = ('main_app', '0003_update_is_clean') + + def prepare(self): + """Prepare some data before the migration.""" + SomeItem = self.old_state.apps.get_model('main_app', 'SomeItem') + SomeItem.objects.create(string_field='a') + SomeItem.objects.create(string_field='a b') + + def test_migration_main0003(self): + """Run the test itself.""" + SomeItem = self.new_state.apps.get_model('main_app', 'SomeItem') + + assert SomeItem.objects.count() == 2 + assert SomeItem.objects.filter(is_clean=True).count() == 1 +``` + +### Choosing only migrations tests + +In CI systems it is important to get instant feedback. Running tests that +apply database migration can slow down tests execution, so it is often a good +idea to run standard, fast, regular unit tests without migrations in parallel +with slower migrations tests. + +#### pytest + +`django_test_migrations` adds `migration_test` marker to each test using +`migrator_factory` or `migrator` fixture. +To run only migrations test, use `-m` option: + +```bash +pytest -m migration_test # runs only migraion tests +pytest -m "not migration_test" # runs all except migraion tests +``` + +#### unittest + +`django_test_migrations` adds `migration_test` +[tag](https://docs.djangoproject.com/en/3.0/topics/testing/tools/#tagging-tests) +to every `MigratorTestCase` subclass. +To run only migrations tests, use `--tag` option: + +```bash +python mange.py test --tag=migration_test # runs only migraion tests +python mange.py test --exclude-tag=migration_test # runs all except migraion tests +``` + + +## Django Checks + +`django_test_migrations` comes with 2 groups of Django's checks for: + ++ detecting migrations scripts automatically generated names ++ validating some subset of database settings + +### Testing migration names + +`django` generates migration names for you when you run `makemigrations`. +And these names are bad ([read more](https://adamj.eu/tech/2020/02/24/how-to-disallow-auto-named-django-migrations/) about why it is bad)! +Just look at this: `0004_auto_20191119_2125.py` + +What does this migration do? What changes does it have? + +One can also pass `--name` attribute when creating migrations, but it is easy to forget. + +We offer an automated solution: `django` check +that produces an error for each badly named migration. + +Add our check into your `INSTALLED_APPS`: + +```python +INSTALLED_APPS = [ + # ... + + # Our custom check: + 'django_test_migrations.contrib.django_checks.AutoNames', +] +``` + +And then in your CI run: + +```bash +python manage.py check --deploy +``` + +This way you will be safe from wrong names in your migrations. + +Do you have a migrations that cannot be renamed? Add them to the ignore list: + +```python +# settings.py + +DTM_IGNORED_MIGRATIONS = { + ('main_app', '0004_auto_20191119_2125'), + ('dependency_app', '0001_auto_20201110_2100'), +} +``` + +And we won't complain about them. + +Or you can completely ignore entire app: + +```python +# settings.py + +DTM_IGNORED_MIGRATIONS = { + ('dependency_app', '*'), + ('another_dependency_app', '*'), +} +``` + +### Database configuration + +Add our check to `INSTALLED_APPS`: + +```python +INSTALLED_APPS = [ + # ... + + # Our custom check: + 'django_test_migrations.contrib.django_checks.DatabaseConfiguration', +] +``` + +Then just run `check` management command in your CI like listed in section +above. + + +## Related projects + +You might also like: + +- [django-migration-linter](https://github.com/3YOURMIND/django-migration-linter) - Detect backward incompatible migrations for your django project. +- [wemake-django-template](https://github.com/wemake-services/wemake-django-template/) - Bleeding edge django template focused on code quality and security with both `django-test-migrations` and `django-migration-linter` on board. + + +## Credits + +This project is based on work of other awesome people: + +- [@asfaltboy](https://gist.github.com/asfaltboy/b3e6f9b5d95af8ba2cc46f2ba6eae5e2) +- [@blueyed](https://gist.github.com/blueyed/4fb0a807104551f103e6) +- [@fernandogrd](https://gist.github.com/blueyed/4fb0a807104551f103e6#gistcomment-1546191) +- [@adamchainz](https://adamj.eu/tech/2020/02/24/how-to-disallow-auto-named-django-migrations/) + + +## License + +MIT. + + +%prep +%autosetup -n django-test-migrations-1.2.0 + +%build +%py3_build + +%install +%py3_install +install -d -m755 %{buildroot}/%{_pkgdocdir} +if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi +if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi +if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi +if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi +pushd %{buildroot} +if [ -d usr/lib ]; then + find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/lib64 ]; then + find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/bin ]; then + find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/sbin ]; then + find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst +fi +touch doclist.lst +if [ -d usr/share/man ]; then + find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst +fi +popd +mv %{buildroot}/filelist.lst . +mv %{buildroot}/doclist.lst . + +%files -n python3-django-test-migrations -f filelist.lst +%dir %{python3_sitelib}/* + +%files help -f doclist.lst +%{_docdir}/* + +%changelog +* Tue Apr 11 2023 Python_Bot <Python_Bot@openeuler.org> - 1.2.0-1 +- Package Spec generated @@ -0,0 +1 @@ +94f2bdf4104387cc58e6af5ffb627384 django-test-migrations-1.2.0.tar.gz |