如何 运行 使用默认和非托管数据库进行 Django 单元测试?
How to run Django units test with default and unmanaged database?
我有一个带有默认数据库的 Django 项目,用于存储用户、订单等内容。
我们还有一个非托管数据库。现在,当您 运行 Django 测试时,他们会尝试创建测试数据库,但由于我们有一个非托管数据库,我们无法执行此操作。我无法创建此数据库的迁移,因为这将导致 300 个关于冲突反向访问器的错误。
我们使用 Docker 并自动启动这个非托管数据库并用一些模拟数据填充它。这个用于开发等。我希望单元测试用这个来测试
我试过创建迁移之类的东西,但由于反向访问器问题,这是不可能的。
有没有办法使用非托管数据库进行单元测试? Django 创建的 test_default 数据库很好,但我无法创建 test_unmanaged 数据库。
我们在同一数据库中使用托管和非托管 table 设置,这可能也适用于您的用例:
我们有一个脚本可以从两个转储生成测试数据库:test_structure.sql
和 test_fixtures.sql
。前者包含数据库在某个时间点的结构,包括所有非托管table。后者包含测试期间非托管 table 中可能需要的任何数据,以及 django_migrations
table 的内容。我们使用生成的 COPY (SELECT * FROM {table}) TO STDOUT
列表转储 test_fixtures.sql
;语句,例如:COPY (SELECT * FROM obs_00.django_migrations) TO STDOUT WITH NULL '--null--';
。
psql -c {copy_statement}
的输出使用如下函数转换为 INSERT
语句:
def csv2sqlinsert(table_name, data):
"""
Convert TSV output of COPY (SELECT * FROM {table}) TO STDOUT
to INSERT INTO {table} VALUES (), ()...();
"""
def is_int(val):
try:
return "{}".format(int(val)) == val
except ValueError:
return False
def column(data):
if data == "--null--":
return "null"
elif is_int(data):
return data
else:
return "'{}'".format(data.replace("'", "''")) # escape quotes
rows = [row.split("\t") for row in data.decode().split("\n") if len(row) > 1]
if len(rows) == 0:
return f"-- no data for {table_name}\n"
data = ",\n".join("({})".format(",".join(column(col) for col in row)) for row in rows)
ret = ""
ret += f"-- {table_name} ({len(rows)} rows)\n"
ret += f"INSERT INTO {table_name} VALUES\n{data};\n"
return ret
实际上这个功能更复杂,它还简化了我们的 postgis 几何图形和 t运行cating large text fields to save space.
创建测试数据库
在settings_test.py
中定义测试数据库名称:
DATABASES["default"].update({
"NAME": "django_test_db",
"TEST": {"NAME": "django_test_db",},
})
使用上面的两个文件,(重新)创建测试数据库如下所示:
dropdb django_test_db
createdb django_test_db
psql -d django_test_db -f test_structure.sql
psql -d django_test_db < test_fixtures.sql
我们现在有了转储时的数据库状态。因为可能会有新的迁移,我们让django迁移:
./manage.py migrate --settings=settings_test
运行 测试
现在我们可以 运行 使用 ./manage.py test --settings=settings_test
进行测试。因为每次测试都重新创建数据库 运行 可能会花费大量时间,所以添加 --keepdb
将为您节省大量等待测试数据库恢复过程的时间。
我们修改了 manage.py
这样我们就不会忘记:
#!/usr/bin/env python
import os
import sys
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "test":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings_test")
cmd = sys.argv + ["--keepdb"]
else:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
cmd = sys.argv
from django.core.management import execute_from_command_line
execute_from_command_line(cmd)
我有一个带有默认数据库的 Django 项目,用于存储用户、订单等内容。
我们还有一个非托管数据库。现在,当您 运行 Django 测试时,他们会尝试创建测试数据库,但由于我们有一个非托管数据库,我们无法执行此操作。我无法创建此数据库的迁移,因为这将导致 300 个关于冲突反向访问器的错误。
我们使用 Docker 并自动启动这个非托管数据库并用一些模拟数据填充它。这个用于开发等。我希望单元测试用这个来测试
我试过创建迁移之类的东西,但由于反向访问器问题,这是不可能的。
有没有办法使用非托管数据库进行单元测试? Django 创建的 test_default 数据库很好,但我无法创建 test_unmanaged 数据库。
我们在同一数据库中使用托管和非托管 table 设置,这可能也适用于您的用例:
我们有一个脚本可以从两个转储生成测试数据库:test_structure.sql
和 test_fixtures.sql
。前者包含数据库在某个时间点的结构,包括所有非托管table。后者包含测试期间非托管 table 中可能需要的任何数据,以及 django_migrations
table 的内容。我们使用生成的 COPY (SELECT * FROM {table}) TO STDOUT
列表转储 test_fixtures.sql
;语句,例如:COPY (SELECT * FROM obs_00.django_migrations) TO STDOUT WITH NULL '--null--';
。
psql -c {copy_statement}
的输出使用如下函数转换为 INSERT
语句:
def csv2sqlinsert(table_name, data):
"""
Convert TSV output of COPY (SELECT * FROM {table}) TO STDOUT
to INSERT INTO {table} VALUES (), ()...();
"""
def is_int(val):
try:
return "{}".format(int(val)) == val
except ValueError:
return False
def column(data):
if data == "--null--":
return "null"
elif is_int(data):
return data
else:
return "'{}'".format(data.replace("'", "''")) # escape quotes
rows = [row.split("\t") for row in data.decode().split("\n") if len(row) > 1]
if len(rows) == 0:
return f"-- no data for {table_name}\n"
data = ",\n".join("({})".format(",".join(column(col) for col in row)) for row in rows)
ret = ""
ret += f"-- {table_name} ({len(rows)} rows)\n"
ret += f"INSERT INTO {table_name} VALUES\n{data};\n"
return ret
实际上这个功能更复杂,它还简化了我们的 postgis 几何图形和 t运行cating large text fields to save space.
创建测试数据库
在settings_test.py
中定义测试数据库名称:
DATABASES["default"].update({
"NAME": "django_test_db",
"TEST": {"NAME": "django_test_db",},
})
使用上面的两个文件,(重新)创建测试数据库如下所示:
dropdb django_test_db
createdb django_test_db
psql -d django_test_db -f test_structure.sql
psql -d django_test_db < test_fixtures.sql
我们现在有了转储时的数据库状态。因为可能会有新的迁移,我们让django迁移:
./manage.py migrate --settings=settings_test
运行 测试
现在我们可以 运行 使用 ./manage.py test --settings=settings_test
进行测试。因为每次测试都重新创建数据库 运行 可能会花费大量时间,所以添加 --keepdb
将为您节省大量等待测试数据库恢复过程的时间。
我们修改了 manage.py
这样我们就不会忘记:
#!/usr/bin/env python
import os
import sys
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "test":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings_test")
cmd = sys.argv + ["--keepdb"]
else:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
cmd = sys.argv
from django.core.management import execute_from_command_line
execute_from_command_line(cmd)