测试django模式和数据迁移,包括排序
项目描述
django-test-migrations
功能
- 允许测试django模式和数据处理迁移
- 允许测试正向和回滚迁移
- 允许测试迁移顺序
- 允许测试迁移名称
- 允许测试数据库配置
- 完全使用注解类型化,并使用mypy检查,与PEP561兼容
- 易于开始:拥有大量文档、测试和教程
安装
pip install django-test-migrations
我们支持几个django版本
- 3.2
- 4.1
- 4.2
- 5.0
其他版本可能也可以正常工作,但它们不受官方支持。
测试Django迁移
在django领域,测试迁移并不常见。但有时这是完全必要的。何时?
当我们进行复杂的模式或数据更改,并确保现有数据不会被损坏时。我们可能还希望确保所有迁移都可以安全回滚。最后,我们希望确保迁移顺序正确,并且具有正确的依赖关系。
测试正向迁移
为了测试所有迁移,我们有一个Migrator类。
它有三个方法可以操作
- 
.apply_initial_migration()接受应用和迁移名称,在实际迁移发生之前生成一个状态。它通过应用所有传递的参数及之前的迁移来创建before state。
- 
.apply_tested_migration()接受应用和迁移名称来执行实际迁移
- 
.reset()在测试完成后清理一切
所以,这里有一个例子
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()
这是一个正向迁移的例子。
逆向迁移
事实上,你也可以测试逆向迁移。除了传递的迁移名称和你的逻辑外,没有什么真正改变。
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()
测试迁移顺序
有时我们还想确保我们的迁移顺序正确,并且所有我们的dependencies = [...]都是正确的。
为了实现这一点,我们有一个plan.py模块。
这是它的用法
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'),
]
这样你就可以确保迁移和相互依赖的应用将按正确的顺序执行。
factory_boy集成
如果你使用工厂创建模型,你可以用factory的方法替换它们各自的.build()或.create()调用,并将模型名称和工厂类作为参数传递
import factory
old_state = migrator.apply_initial_migration(
    ('main_app', '0002_someitem_is_clean'),
)
SomeItem = old_state.apps.get_model('main_app', 'SomeItem')
# instead of
# item = SomeItemFactory.create()
# use this:
factory.create(SomeItem, FACTORY_CLASS=SomeItemFactory)
# ...
测试框架集成 🐍
我们支持几个测试框架作为一等公民。毕竟,这是一个测试工具!
请注意,在测试开始时,Django post_migrate信号的接收者列表被清除,之后恢复。如果你需要测试自己的post_migrate信号,则在测试期间附加/删除它们。
pytest
我们提供的django-test-migrations附带一个pytest插件,它提供了两个方便的固定装置
- migrator_factory为你提供了创建任何数据库的- Migrator类的机会
- migrator实例为- 'default'数据库
这是它的用法
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):
        # Model 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
我们还提供与内置的unittest框架的集成。
这是它的用法
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
仅选择迁移测试
在CI系统中,获得即时反馈很重要。运行应用数据库迁移的测试可能会减慢测试执行速度,因此通常在并行运行较慢的迁移测试的同时运行标准的、快速的常规单元测试是一个好主意。
pytest
django_test_migrations给每个使用migrator_factory或migrator固定装置的测试添加了migration_test标记。要仅运行迁移测试,使用-m选项
pytest -m migration_test  # Runs only migration tests
pytest -m "not migration_test"  # Runs all except migration tests
unittest
django_test_migrations给每个MigratorTestCase子类添加了migration_test 标记。要仅运行迁移测试,使用--tag选项
python mange.py test --tag=migration_test  # Runs only migration tests
python mange.py test --exclude-tag=migration_test  # Runs all except migration tests
Django Checks
django_test_migrations 带有两组 Django 检查,用于
- 自动检测迁移脚本生成的名称
- 验证数据库设置的一部分
测试迁移名称
django 在您运行 makemigrations 时为您生成迁移名称。这些名称不好(了解更多为什么它不好!)(阅读更多关于为什么它不好!): 0004_auto_20191119_2125.py
这个迁移做了什么?它有哪些更改?
也可以在创建迁移时传递 --name 属性,但很容易忘记。
我们提供了一个自动解决方案:生成错误并针对每个名称不正确的迁移进行检查的 django 检查。
将我们的检查添加到您的 INSTALLED_APPS
INSTALLED_APPS = [
    # ...
    # Our custom check:
    'django_test_migrations.contrib.django_checks.AutoNames',
]
然后在您的 CI 中运行
python manage.py check --deploy
这样,您就可以避免迁移中的错误名称。
您有无法重命名的迁移吗?将它们添加到忽略列表
# settings.py
DTM_IGNORED_MIGRATIONS = {
    ('main_app', '0004_auto_20191119_2125'),
    ('dependency_app', '0001_auto_20201110_2100'),
}
然后我们不会抱怨它们。
或者您可以选择完全忽略整个应用程序
# settings.py
DTM_IGNORED_MIGRATIONS = {
    ('dependency_app', '*'),
    ('another_dependency_app', '*'),
}
数据库配置
将我们的检查添加到 INSTALLED_APPS
INSTALLED_APPS = [
    # ...
    # Our custom check:
    'django_test_migrations.contrib.django_checks.DatabaseConfiguration',
]
然后只需在您的 CI 中运行 check 管理命令,如上所述。
相关项目
您可能还喜欢
- django-migration-linter - 检测 django 项目的向后不兼容迁移。
- wemake-django-template - 针对代码质量和安全性的 bleeding edge django 模板,集成了 django-test-migrations和django-migration-linter。
致谢
本项目基于其他优秀人士的工作
许可证
MIT。
项目详情
下载文件
为您的平台下载文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。
源代码分布
构建分布
散列 对于 django_test_migrations-1.4.0-py3-none-any.whl
| 算法 | 散列摘要 | |
|---|---|---|
| SHA256 | 294dff98f6d43d020d4046b971bac5339e7c71458a35e9ad6450c388fe16ed6b | |
| MD5 | c146a866a8c68b3dc869b60b6c22c765 | |
| BLAKE2b-256 | 546011c792485df2cc242bb13ab167dcd3cc1ff1d943bcb235b8320dd71c6bf6 |