如何测试主要使用外部API的代码?

如何测试主要使用外部API的代码?,api,unit-testing,testing,integration-testing,Api,Unit Testing,Testing,Integration Testing,我最近写了一些代码,发现编写有用的测试几乎是不可能的 这是一个cronjob,它实现了以下功能: 从外部服务(AWS cognito)获取用户 通过询问系统的内部API来检查用户的某些属性 根据业务逻辑向用户发送邮件(AWS SES) 更新用户属性(AWS cognito) 总共大约有100行python代码。为了测试这一点,通常的建议似乎是检查API的格式并在测试中模拟它们。我可以为我的“快乐之路”这样做,但我真的没有办法知道在所有情况下和故障模式下数据将如何传输,因为涉及到这么多外部服务

我最近写了一些代码,发现编写有用的测试几乎是不可能的

这是一个cronjob,它实现了以下功能:

  • 从外部服务(AWS cognito)获取用户
  • 通过询问系统的内部API来检查用户的某些属性
  • 根据业务逻辑向用户发送邮件(AWS SES)
  • 更新用户属性(AWS cognito)
总共大约有100行python代码。为了测试这一点,通常的建议似乎是检查API的格式并在测试中模拟它们。我可以为我的“快乐之路”这样做,但我真的没有办法知道在所有情况下和故障模式下数据将如何传输,因为涉及到这么多外部服务

如果代码接收到一些意外的数据,我会认为代码会失败,但是没有办法测试它,或者是真的吗

我已经在一个更大的系统中使用pact进行了消费者驱动的契约测试,但在这里,我只能针对我们控制的内部API使用它,而对于这样一个小脚本来说,实现这一点所需的基础设施似乎太多了

我们当前的方法是监视脚本的错误,并在第二天在办公室修复它。从业务规则的角度来看,如果脚本在几天内没有运行,这不是问题。你认为这是最好的方法吗


您将如何测试这一点?

tldr;根据资源、优先级和关键性解决方案,监控和一些单元测试可能是一个很好的选择


在这种情况下,我会尝试解耦组件。它将允许我独立测试所有组件,并简化最终测试逻辑

我们从例子中考虑Cron Joobe。伪代码可能与下一个类似:

def main():
用户:列表[用户]=获取用户()
过滤的\u用户=检查\u属性(用户)
业务逻辑发送电子邮件(过滤用户)
更新\u属性(筛选的\u用户)
我将逻辑划分为不同的函数,现在我可以分别测试每个函数(甚至是main)

fetch_用户
负责与外部系统交互,并将其响应转换为预定义的用户模型。如果出现任何错误,
fetch\u用户应处理该错误。根据外部系统的不同,我可以使用集成测试或使用模拟/存根的单元测试来测试此功能。每种选择都有其优缺点。您提到您有监控功能,并且功能并不重要,因此使用mock/stub和监控的单元测试将非常适合。即使我们在模拟测试中错过了一些负面场景,并且在生产中失败了,监控也会捕捉到它,通过一个单独的
fetch\u users
函数,我们可以对其进行本地化和复制,然后我们可以轻松地用这个新案例扩展我们的测试

使用
check_properties
我们也可以做同样的事情——用integ测试或mock/stub测试它。这里最重要的是,我们不需要调用
fetch\u users
,我们可以将测试用户直接转移到
check\u properties
。正如您所提到的
check\u properties
调用内部api,因此我们可以在这里使用消费者驱动的契约测试(如果已经存在),或integ测试或任何其他保证系统之间契约的测试(即通过swagger/openapi规范进行验证)

business\u logic\u发送电子邮件
-从业务的角度来看,这个功能可能是最重要的,所以我们需要用单元(func)测试覆盖所有业务逻辑。幸运的是,我们不需要向任何真正的外部系统发出请求(获取用户),我们只需通过发送电子邮件将测试用户直接传输到
业务逻辑。我们甚至不需要检查真正的电子邮件发送,只需检查我们调用发送电子邮件的函数(发送真正的电子邮件,我们可以在单独的集成测试中检查)
使用
update\u属性
与使用
fetch\u用户
的情况相同


最后一个是
main
。虽然我们单独测试了所有函数,但不能保证main中的所有函数都被正确调用。在像示例伪代码这样简单的函数中,可能不需要对其进行测试(因为没有任何复杂的逻辑),但在复杂的情况下,最好检查“快乐路径”和少量的负面代码。它可以用mock或IoC/DI机制来完成。tldr;根据资源、优先级和关键性解决方案,监控和一些单元测试可能是一个很好的选择


在这种情况下,我会尝试解耦组件。它将允许我独立测试所有组件,并简化最终测试逻辑

我们从例子中考虑Cron Joobe。伪代码可能与下一个类似:

def main():
用户:列表[用户]=获取用户()
过滤的\u用户=检查\u属性(用户)
业务逻辑发送电子邮件(过滤用户)
更新\u属性(筛选的\u用户)
我将逻辑划分为不同的函数,现在我可以分别测试每个函数(甚至是main)

fetch_用户
负责与外部系统交互,并将其响应转换为预定义的用户模型。如果出现任何错误,
fetch\u用户应处理该错误。根据外部系统的不同,我可以使用集成测试或使用模拟/存根的单元测试来测试此功能。每种选择都有其优缺点。您提到您有监控功能,并且功能并不重要,因此使用mock/stub和监控的单元测试将非常适合。即使我们在模拟测试中错过了一些负面场景,并且在生产中失败了,监控也会捕捉到它,通过一个单独的
fetch\u users
函数,我们可以对其进行本地化和复制,然后我们可以轻松地用这个新案例扩展我们的测试

使用
检查属性
我们可以