You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python无返回值函数单元测试:添加测试专用代码是否为不良实践?

关于Django无返回值函数的测试实践问题

问题描述

本人正在自学编程,使用Django开发模拟拍卖网站,目前正学习为软件编写测试用例。遇到的问题是:Web应用中的多个函数通常无返回值,例如cleantags函数。为测试该函数,我为其添加了一个布尔参数testing,当参数为True时,函数会返回被删除的标签,以便在unittest中进行断言验证。

目标函数代码

def cleantags(testing=False) -> None:
    """Executed on listing update, delete, or accept.
    Purges tags in 'Category' that are no longer used"""
    unused_tags = Category.objects.filter(Q(listing__isnull=True) | Q(listing__sold=True))
    for _tag in unused_tags:
        _tag.delete()

    if testing:
        deleted_tags = []
        for _tag in unused_tags:
            deleted_tags.append(_tag.__str__())
        return deleted_tags

现有测试代码

def test_tag_deleted(self):
    """Checking that cleantags() will purge tags in Catergories
    that don't belong to a listing or to one that has sold."""
    # ['Lions','Tigers','Bears','Foo','Bar']
    tag_objs = self.generate_tags()
    self.listing1.tags.add(tag_objs[0])
    self.listing1.tags.add(tag_objs[1])

    self.listing2.tags.add(tag_objs[2])
    self.listing2.sold = True
          
    del_tags = listmng.cleantags(True)
    self.assertListEqual(del_tags, ['Foo','Bar'])

现咨询:这种在函数中添加测试专用代码的做法是否属于不良编程实践?如果是,有没有更优方法确保cleantags这类函数的功能符合预期?


回答

这种做法属于不良编程实践吗?

是的,给生产函数添加测试专用参数和分支的做法并不推荐,核心原因包括:

  • 污染生产代码:测试逻辑不属于业务需求,硬塞进生产函数会增加代码复杂度,提升维护成本,其他开发者阅读时需要额外理解非业务相关的分支逻辑。
  • 潜在生产风险:如果测试分支存在逻辑漏洞,或者有人误在生产环境中传入testing=True,可能会引发意外的功能异常。
  • 违背单一职责:cleantags的核心职责是清理无用标签,额外添加返回测试数据的功能,偏离了函数的设计定位。

更优的测试方案

针对无返回值的函数,我们可以通过验证函数执行后的系统状态来确保功能正确性,而非修改生产代码适配测试,具体方案如下:

1. 直接验证数据库状态

测试时先准备好预设数据,执行cleantags后,直接查询数据库验证标签的存在/缺失状态:

def test_tag_deleted(self):
    # 准备测试数据
    tag_objs = self.generate_tags()
    self.listing1.tags.add(tag_objs[0], tag_objs[1])
    self.listing2.tags.add(tag_objs[2])
    self.listing2.sold = True
    self.listing2.save()  # 务必保存修改,确保数据库状态更新

    # 执行清理函数
    listmng.cleantags()

    # 验证预期被删除的标签已不存在
    self.assertFalse(Category.objects.filter(name='Foo').exists())
    self.assertFalse(Category.objects.filter(name='Bar').exists())
    # 验证应保留的标签仍存在
    self.assertTrue(Category.objects.filter(name='Lions').exists())
    self.assertTrue(Category.objects.filter(name='Tigers').exists())
    self.assertTrue(Category.objects.filter(name='Bears').exists())

2. 拆分函数逻辑,降低测试耦合

将cleantags的查询逻辑和删除逻辑拆分为两个独立函数,分别测试:

def get_unused_tags():
    """返回所有需要清理的无用标签集合"""
    return Category.objects.filter(Q(listing__isnull=True) | Q(listing__sold=True))

def cleantags():
    """清理所有无用标签"""
    unused_tags = get_unused_tags()
    unused_tags.delete()

测试时可以先单独验证get_unused_tags是否返回正确的标签列表,再测试cleantags是否成功删除这些标签,逻辑更清晰,测试粒度更精准。

3. 利用Django测试框架的特性

Django的TestCase类自带数据库事务回滚机制,每个测试用例执行后都会自动重置数据库状态,无需手动清理测试数据,可以放心地在测试中创建、修改和删除数据,避免测试用例之间的相互干扰。


内容的提问来源于stack exchange,提问作者RockBass

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 11:09:56