tldr;根据资源、优先级和关键性解决方案以及监控和一些单元测试可能是一个不错的选择。
在这种情况下,我会尝试解耦组件。它将允许我独立测试所有组件并简化最终测试逻辑。
让我们从示例中考虑 cronjob。可能伪代码看起来像下一个:
def main():
users: List[Users] = fetch_users()
filtered_users = check_properties(users)
business_logic_with_sending_emails(filtered_users)
update_properties(filtered_users)
我在单独的函数上划分逻辑,现在我可以单独测试每个函数(甚至是主函数)
fetch_users 负责与外部系统交互并将其响应转换为预定义的用户模型。如果发生任何错误,fetch_users 应该处理它。根据外部系统,我可以使用集成测试或使用模拟/存根的单元测试来测试此功能。每个选项都有自己的优点和缺点。你提到你有监控和功能并不重要,所以带有模拟/存根的单元测试和监控将是一个很好的选择。即使我们在模拟测试中错过了一些负面场景并且它在生产中失败,监控也会捕获它,使用单独的fetch_users 函数我们可以本地化和重现它,然后我们可以轻松地使用这个新案例扩展我们的测试。
使用check_properties,我们可以做同样的事情——使用集成测试或模拟/存根测试它。这里主要是我们不需要调用fetch_users,我们可以直接将测试用户转移到check_properties。正如你提到的check_properties 调用内部 api,所以我们可以在这里使用消费者驱动的合同测试(如果它已经存在),或者集成测试或任何其他保证系统之间合同的测试(即通过 swagger/openapi 规范进行验证)
business_logic_with_sending_emails — 从业务的角度来看,这个功能可能是最重要的,所以我们需要用单元(func)测试来覆盖所有的业务逻辑。幸运的是,我们不需要向任何真实的外部系统发出请求(获取用户),我们只需将测试用户直接转移到business_logic_with_sending_emails。我们甚至不需要检查真实的电子邮件发送,检查我们调用了发送电子邮件的函数就足够了(发送真实的电子邮件我们可以在单独的集成测试中检查)
update_properties 与 fetch_users 的故事相同。
最后一个是main。虽然我们分别测试了所有函数,但不能保证 main 中的所有函数都被正确调用。可能在示例伪代码这样简单的函数中,不需要对其进行测试(因为没有任何复杂的逻辑),但在复杂的情况下,最好检查“快乐路径”并且很少有负面影响。它可以通过模拟或 IoC/DI 机制来完成。