Skip to main content
 首页 » 编程设计

python之如何使用默认和非托管数据库运行 Django 单元测试

2024年08月14日44exmyth

我有一个带有默认数据库的 Django 项目,用于存储用户、订单等内容。

我们还有一个非托管数据库。现在,当您运行 Django 测试时,他们会尝试创建测试数据库,但由于我们有一个非托管数据库,我们无法执行此操作。我无法创建此数据库的迁移,因为这将导致 300 个关于冲突反向访问器的错误。

我们使用 Docker 并自动启动这个非托管数据库并用一些模拟数据填充它。这个用于开发等。我希望单元测试使用这个进行测试。

我尝试过创建迁移之类的操作,但由于反向访问器问题,这是不可能的。

有没有办法使用非托管数据库进行单元测试? Django 创建的 test_default 数据库没问题,但我无法创建 test_unmanaged 数据库。

请您参考如下方法:

我们在同一数据库中使用托管表和非托管表的设置,这可能也适用于您的用例:

我们有一个脚本可以从两个转储生成测试数据库:test_structure.sql 和 test_fixtures.sql。前者包含数据库在某个时间点的结构,包括所有非托管表。后者包含测试期间非托管表中可能需要的任何数据,以及 django_migrations 表的内容。我们使用生成的 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 几何图形并截断了大文本字段以节省空间。

创建测试数据库

在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)