我正在为计算机视觉论文编写一个相当复杂的机器学习程序.它工作得很好,但我需要继续尝试新的东西并添加新的功能.这是有问题的,因为我在扩展代码或尝试简化算法时有时会引入错误.
显然,正确的做法是添加单元测试,但目前尚不清楚如何做到这一点.我的程序的许多组件产生了一些主观的答案,我无法自动进行健全性检查.
例如,我有一些代码使用较低分辨率的曲线近似曲线,因此我可以在较低分辨率的曲线上进行计算密集型工作.我不小心在这段代码中引入了一个错误,只有当我整个程序的结果稍差时,才会通过艰苦的搜索找到它.
但是,当我尝试为它编写单元测试时,我不知道应该做什么.如果我制作一个具有明确正确的低分辨率版本的简单曲线,那么我并没有真正测试出错的一切.如果我做一个简单的曲线,然后稍微扰乱点,我的代码开始产生不同的答案,即使这个特定的代码片段现在似乎真的很好.
"那我就不是在测试可能出错的一切."
正确.
单元测试的工作不是测试可能出错的一切.
单元测试的工作是根据具体的输入和特定的预期结果来测试你所拥有的是正确的.这里的重要部分是具体的可见外部要求,具体测试用例满足.并非所有可能出错的事都以某种方式被阻止.
什么都不能测试可能出错的一切.你可以写一个证明,但是你很难为所有东西编写测试.
明智地选择您的测试用例.
此外,单元测试的工作是测试整个应用程序的每个小部分都做正确的事情 - 孤立地.
例如,您的"使用较低分辨率曲线近似曲线的代码"可能有几个小部件,可以作为单独的单元进行测试.处于隔离状态.整合的整体也可以进行测试,以确保它的工作原理.
例如,您对"低分辨率曲线的计算密集型工作"可能有几个小部件可以作为单独的单元进行测试.处于隔离状态.
单元测试的这一点是创建后来组装的小型,正确的单元.
没有看到你的代码,很难说,但我怀疑你是在试图在太高的水平上编写测试.您可能想要将您的方法分解为确定性的小组件并对其进行测试.然后通过提供从底层方法(可能位于不同对象)返回可预测值的模拟实现来测试使用这些方法的方法.然后,您可以编写涵盖各种方法领域的测试,确保您覆盖所有可能的结果.对于小方法,您可以通过提供表示输入域的值来实现.对于依赖于这些的方法,通过提供从依赖项返回结果范围的模拟实现.
你可能不会体会到讽刺,但基本上你所拥有的是遗留代码:一大堆没有任何单元测试的软件.当然你不知道从哪里开始.因此,您可能会发现阅读处理遗留代码很有帮助.
关于这一点的最终想法是Michael Feather的书"有效地使用遗留代码".在ObjectMentor网站上曾经有过一个有用的摘要,但是网站已经走了公司的道路.然而,WELC在评论和其他文章中留下了遗产. 检查出来(或者只是买书),虽然关键课程是S.Lott和tvanfosson在回复中提到的.
您的单元测试需要使用某种模糊因子,通过接受近似值或使用某种概率检查.
例如,如果您有一些返回浮点结果的函数,则几乎不可能编写在所有平台上都能正常工作的测试.您的检查需要执行近似.
TEST_ALMOST_EQ(result, 4.0);
以上TEST_ALMOST_EQ
可能会验证result
介于3.9和4.1之间(例如).
或者,如果您的机器学习算法是概率性的,那么您的测试需要通过获取多次运行的平均值并期望它在某个范围内来适应它.
x = 0; for (100 times) { x += result_probabilistic_test(); } avg = x/100; TEST_RANGE(avg, 10.0, 15.0);
当然,测试是非确定性的,因此您需要对它们进行调整,以便您可以很高的概率进行非片状测试.(例如,增加试验次数,或增加误差范围).
您也可以使用模拟(例如,用于概率算法的模拟随机数生成器),它们通常有助于确定性地测试特定的代码路径,但它们需要付出很多努力才能维护.理想情况下,您将使用模糊测试和模拟的组合.
HTH.