单元测试和测试驱动开发的策略

计算科学 测试
2021-11-29 21:57:41

我是科学计算中测试驱动开发的大力倡导者。它在实践中的实用性是惊人的,并且真正缓解了代码开发人员所知道的经典问题。但是,在测试科学代码时存在一些在一般编程中不会遇到的固有困难,因此 TDD 文本作为教程并不是非常有用。例如:

  • 一般来说,您不知道先验给定复杂问题的确切答案,那么您如何编写测试呢?

  • 并行度变化;我最近遇到了一个错误,使用 MPI 任务作为 3 的倍数会失败,但 2 的倍数有效。此外,由于 MPI 的本质,常见的测试框架似乎对 MPI 不太友好——您必须重新执行测试二进制文件来更改任务的数量。

  • 科学代码通常有很多紧密耦合、相互依赖和可互换的部分。我们都看过遗留代码,我们知道放弃良好的设计并使用全局变量是多么诱人。

  • 通常,数值方法可能是一种“实验”,或者编码人员不完全理解它的工作原理并试图理解它,因此无法预测结果。

我为科学代码编写的一些测试示例:

  • 对于时间积分器,请使用具有精确解的简单 ODE,并测试您的积分器是否在给定精度内求解,并且通过使用不同步长进行测试,精度顺序是否正确。

  • 零稳定性测试:检查边界/初始条件为 0 的方法是否保持为 0。

  • 插值测试:给定一个线性函数,确保插值是正确的。

  • 遗留验证:隔离遗留应用程序中已知正确的代码块,并提取一些离散值用于测试。

除了手动试错之外,我仍然经常无法弄清楚如何正确测试给定的代码块。您能否提供一些您为数字代码编写的测试示例,和/或测试科学软件的一般策略?

3个回答

制造解决方案的方法

通过细化研究验证该方法达到了理论精度。

保守答案。解决方案的按位和按范数再现。

比尔已经列出了一些解决您问题的方法。

解决您的第三点,不,没有理由在部件之间引入强耦合。恰恰相反:如果您的函数或类具有定义良好的接口,那么将线性求解器替换为另一个求解器或时间步进方案会更容易。只要抵制它,然后您就可以分别测试这些组件。几十年来,我们已经使用 deal.II 做到了这一点。

对于您的第四点:如果您的方法是一个实验,那么您对该方法的实验就构成了一个测试。只要您没有分析,您就必须将这些测试结果视为最佳可用。但通常,您对方法的顺序有一个期望,或者您会知道它对于某一类解决方案是精确的,例如达到一定程度的多项式。验证这些应该是您实验的一部分,并且随着分析的改进,可以添加测试。

我最近在计算科学中发现了这篇关于 TDD 的论文。我还没有读过它,所以我不知道它是否有任何好处,但希望它可以有所帮助。

http://cyber.ua.edu/files/2014/12/u0015_0000001_0001551.pdf