关于单元测试的两个问题。
我已经写了一段时间的单元测试,但是它们通常是为了测试我已经写过的类。最近我读到一篇文章 (请注意是一篇旧文章),它说你应该在开始编写代码之前编写单元测试。
有没有人真正遵循这种方法?这在纸面上似乎是个好主意,但在实践中是这样吗?
- 您是否应该编写单元测试来查看您的方法如何处理不良/恶意输入?显然,您可能希望针对专门用于处理“用户”输入的函数编写测试,以查看它如何处理错误/恶意输入,但是那些不应该将此类输入传递给它们的函数呢?你在什么时候画线?
关于单元测试的两个问题。
我已经写了一段时间的单元测试,但是它们通常是为了测试我已经写过的类。最近我读到一篇文章 (请注意是一篇旧文章),它说你应该在开始编写代码之前编写单元测试。
有没有人真正遵循这种方法?这在纸面上似乎是个好主意,但在实践中是这样吗?
在课前编写单元测试的方法称为测试驱动开发(TDD),由 Kent Beck 在 2000 年代初期推广。这个想法是你编写一个描述你需要的功能的测试。最初,此测试将失败。当您编写课程时,测试通过了。您重构您的测试以添加更多所需的功能,然后重构该类以使这个新测试通过。测试通过后,您的班级就达到了目标。当然,这也可以扩展到课程之外。
至于要编写什么类型的测试,这取决于您是在测试公共 API 还是私有 API。公共 API 应该编写更广泛的测试以确保输入格式正确,尤其是在您不完全信任 API 的用户的情况下。如果没有这些测试,私有 API(仅由您的代码调用的方法)可能会逃脱 - 我怀疑您可以相信自己的开发团队不会将不良数据传递给他们。
有没有人真正遵循这种方法?
是的。
这在纸面上似乎是个好主意,但在实践中是这样吗?
是的。
您是否应该编写单元测试来查看您的方法如何处理不良/恶意输入?
是的。
那些不应该将这种类型的输入传递给它们的函数呢?你在什么时候画线?
当它从软件转移到精神病时。
你可以——如果你愿意——为不可能的情况编写测试。然而,你显然在浪费你的时间和你的雇主的时间。
您为定义的用例编写测试。就是这样。
您不会根据自己的想象来编造随机测试用例。
如果? 如果定义的用例不完整怎么办?真可惜。您为官方的、合同的、公共的接口编写测试——仅此而已。
如果设计不充分并且您意识到给定的接口充满了不完整的规范、矛盾和安全漏洞怎么办?这与测试无关。这只是编程。糟糕的设计就是糟糕的设计。
如果某些恶意反社会者获取您的代码并以超出(或不符合)定义规范的方式使用它怎么办?真可惜。反社会者获胜。他们能够将您的代码置于您未测试的不可能的情况下。给他们买啤酒,因为他们太聪明了。
首先编写单元测试是很常见的做法。一个很大的好处是,您不只是编写代码将通过的测试,而是定义什么是重要的、您想要实现的以及您想要确保不会发生的测试。它可以帮助您充实您的设计。此外,您可以在编码之前与外部利益相关者一起审查斑点。
至于要写什么测试,根据你的时间,这有点主观。我不会为它永远不会面临的场景疯狂审查代码。也就是说,令人惊讶的是,输入使代码“永远不会看到它”。因此,更多的测试会更好,但在某些时候肯定会出现收益递减。
您编码的语言很重要。动态语言需要更多的测试,因为编译器将捕获更少的问题并且错误可能更难以跟踪(因为它们可以从初始输入问题进一步传播)。至少,这是我的看法。
输入的来源也有所不同。公众应该被认为是恶意的(即网络),员工应该被认为是无能的,甚至应该认为其他编码人员(和你自己!)至少应该被认为是粗心的。但是,当你接近你的核心圈子时,危险就会降低。
测试驱动开发是一个非常普遍的概念。基本思想是您尝试只编写满足软件某些要求所必需的代码。因此,您为需求编写了一个测试,然后编写了使测试通过的代码。
我个人不使用 TDD,但我认识使用 TDD 的人。我个人的想法是,如果您正在处理更多由应用程序驱动的东西,例如数据库或用户界面,它会非常有用。但是,对于更重算法的东西(比如物理模型),我发现它打破了我的思路并妨碍了我。
测试前和测试后的思维方式有所不同。之前编写测试是一种设计形式,因为您正在设计代码的界面并定义预期的行为。然后,当您编写通过测试的代码时,就可以验证您的设计。在开发结束时,您碰巧已经有了一套测试!
使用后测试,您需要小心避免编写现有代码将通过的测试的陷阱。这是一个不同的重点,你不会像测试前的版本那样从中得到更多。
已经有很多很好的答案,但我想对第 2 个问题再多说一点。
如果您使您的单元测试代码“数据驱动”,那么代码是测试“坏”还是“好”输入应该无关紧要。重要的是你有足够大的数据集来涵盖两者。