Python 编写只测试函数是否被调用的单元测试有意义吗?

Python 编写只测试函数是否被调用的单元测试有意义吗?,python,unit-testing,testing,Python,Unit Testing,Testing,我有以下代码: def任务\u已完成(任务): _更新_状态(任务,任务状态。已完成) 成功的任务(任务) def任务_挂起(任务): _更新_状态(任务,任务状态。挂起) 成功的任务(任务) def任务_已取消(任务): _更新_状态(任务,任务状态。已取消) 进程任务(任务) def成功_任务(任务): 进程任务(任务) 发送通知(任务) def过程_任务(任务): 分配用户(任务) 通知用户(任务) 清理(任务) 定义更新状态(任务、状态): task.status=状态 保存(更新_字段

我有以下代码:

def任务\u已完成(任务):
_更新_状态(任务,任务状态。已完成)
成功的任务(任务)
def任务_挂起(任务):
_更新_状态(任务,任务状态。挂起)
成功的任务(任务)
def任务_已取消(任务):
_更新_状态(任务,任务状态。已取消)
进程任务(任务)
def成功_任务(任务):
进程任务(任务)
发送通知(任务)
def过程_任务(任务):
分配用户(任务)
通知用户(任务)
清理(任务)
定义更新状态(任务、状态):
task.status=状态
保存(更新_字段=['status'])
我编写了以下测试:

def测试任务完成(模拟机,任务):
mock\u successful\u task=mocker.patch('services.successful\u task'))
任务完成(任务)
断言task.status==TaskStatus.COMPLETED
模拟成功的任务。使用(任务)调用一次
def测试任务挂起(模拟机、任务):
mock\u successful\u task=mocker.patch('services.successful\u task'))
待决任务(任务)
断言task.status==TaskStatus.PENDING
模拟成功的任务。使用(任务)调用一次
def测试任务已取消(模拟机,任务):
mock\u process\u task=mocker.patch('services.process\u task'))
待决任务(任务)
断言task.status==TaskStatus.cancelled
模拟进程任务。使用(任务)调用一次任务
def测试任务成功(模拟,任务):
mock\u process\u task=mocker.patch('services.process\u task'))
mock\u send\u notification=mocker.patch('notifications.send\u notification'))
模拟进程任务。使用(任务)调用一次任务
模拟发送通知。使用(任务)调用一次通知
def测试过程任务(模拟机,任务):
mock\u assign\u user=mocker.patch('users.assign\u user'))
mock\u notify\u user=mocker.patch('notifications.notify\u user'))
mock\u cleanup=mocker.patch('utils.cleanup'))
模拟分配用户。使用(任务)调用一次
模拟通知用户。使用(任务)调用一次
模拟清理。使用(任务)调用一次
正如您所看到的,一些测试,如
test\u successful\u task
test\u process\u task
只是测试是否调用了特定的函数


但是,为这个编写一个测试有意义吗?或者我理解了一些错误,而我的单元测试很糟糕吗?我不知道应该如何测试这些函数的另一种解决方案。

根据我的经验,这样的测试非常脆弱,因为它们依赖于实现细节。单元测试应该只关注被测试方法的结果。理想情况下,这意味着对返回值进行断言。如果有副作用,您可以断言它们。但是你可能应该看看这些副作用,找到一个不需要它们的不同解决方案。

是的,这是有道理的。然而,我想看看这并不完全是单元测试的目的,尽管它确实有用途。单元测试的目的是通过测试功能和结果来提高代码的质量——编写单元测试来测试调用的每个方法的功能会更加有益


话虽如此,如果您有一个函数调用其他4个函数,并且您想检查它们是否在主代码块中实际执行,那么这是有意义的。但是您也应该为您的子方法编写单元测试。

白盒测试对于检测某些回归或断言已执行特定操作非常有用。
例如,您可以验证在这种特定情况下您没有与数据库交互,或者您已经正确调用了通知服务

然而,缺点是当您更改代码时可能会更改测试,因为您的测试与实现紧密相连。
重构时这可能会很痛苦,因为您还需要重构测试。您可能会忘记断言或步骤,而使用回归创建假阳性测试

我只会在它有意义的时候使用它,如果你需要它来断言细节上发生了什么

您可以在网上搜索TDD:伦敦vs底特律。

你会发现有趣的东西。

我会说不,它们没用

单元测试应该测试功能,这里有一些输入,我称之为,这是我的结果,它是否符合我的预期?事情需要明确和可核实

当您有一个验证方法已被调用的测试时,您真正拥有的是什么? 纯粹的不确定性。好的,已经调用了一个东西,但是它有什么用处呢?你没有验证一个结果,你调用的方法可以做很多事情,你不知道它做了什么

调用方法的代码是一个实现细节,单元测试不应该具备这种知识

为什么我们要编写单元测试? -检查功能 -帮助重构

如果每次代码更改时都需要更改测试,那么您还没有真正完成单元测试的主要原因之一

如果您的代码发生了更改,并且不再调用该方法,那么该怎么办? 你现在必须去改变测试了吗?把它改成什么?如果你的下一步行动是取消测试,那么你首先为什么要这么做

6个月后,如果其他人不得不处理这个问题,该怎么办?没有文档可供检查,为什么要对调用的方法进行测试检查

底线是,这样的测试是零值的,它所做的只是引入不确定性

根据我的经验,是的

当你设计一个测试时,你知道你必须处理4个元素

  • 先决条件(上下文)
  • 投入
  • 输出
  • 后遗症(副作用)
我们都同意,如果被测函数的beahviour仅依赖于输入和输出,那么测试和编码将更加容易,但在某些情况下,这是不可能发生的,尤其是当您的逻辑处理I/O和/或其目标是发出appli的变异时
public class UserService
{
    public void addUser(User toAdd)
}