我们假设,我正在测试以下类(〜伪代码):
// (...)
public Result Process(Image image)
{
Image image2 = PreprocessImage(image);
PartialResult r1 = Process1(image2);
PartialResult r2 = Process2(r1);
Result result = FinalProcessing(r2);
return result;
}
public Image PreprocessImage(Image image)
{
Image tmp1 = Resize(image);
Image tmp2 = Blur(tmp1);
Image tmp3 = Median(tmp2);
Image tmp4 = ExtractSpecificAreas(tmp3);
return tmp4;
}
public Image Median(Image image)
{
// Actual image median algorithm
}
对于问题更多......有问题,让我们假设,大多数这些方法(例如,Process1,Process2,FinalProcessing,ExtractSpecificAreas)的结果都难以预测 - 例如,有一些启发式/决定性的试图从图像中提取特征的算法:它可以在90%的情况下成功,这是可以接受的。
您将对这些方法中的哪一种进行单元测试?除无效的输入/边界条件外,您如何对这些方法进行单元测试?该方法对于单元测试有意义(或多么复杂)有意义吗?
答案 0 :(得分:7)
单元测试的一般规则是测试可以测试的最小可能部分。一个好的规则是每个测试都应该使用公共API中的一个方法。
这意味着它应该只执行这种方法而不是其他方法,甚至不是短暂的。因此,如果您要测试foo()
并调用bar()
,那么您应该模拟bar()
而不是测试它。但是,调用内部私有方法baz()
也没关系。
如果您的方法调用了数百个内部方法,则需要refactoring。
原因是单元测试失败应该指向问题的确切位置。如果您要进行单元测试main()
,那么失败只会告诉您项目代码中存在某个错误。如果你进行单元测试,比如String.length()并且测试失败了,你就会知道错误的确切位置。
这也回答了你的问题:你的方法会返回不可预知的结果。模拟它们将允许您始终返回已知的好/坏结果,因此您可以测试正确处理这些结果的方法。
对于不可预测的方法,您必须找到类似的策略。我在这里假设你有一些训练有素的神经网络,例如。因此,测试可能是将一些训练图像传递给方法N次,直到您确信图像在90%的时间内正确排序。
您应该能够将这些方法拆分为可预测和不可预测的部分,然后您可以使用模拟或统计分析进行测试。
答案 1 :(得分:1)
我认为Aaron Digulla已经指出了最重要的事情:测试最小的碎片并使用模拟物体。
但是,我还要补充一点:您可能还想测试整个应用程序,即小块是否能够很好地协同工作(这将是集成测试而不是单元测试,但无论如何 - 你应该两者兼顾)。为了测试启发式算法,我发现从 easy 任务开始解决它很有用 - 在你的情况下,这将是一种“简单”的图像。当然,这只会给你一个基线,并不能保证它在90%的情况下有效。但是无论如何它在开发过程中都很有用,如果你愿意的话,你可以随时使用更逼真的样本来增强你的(集成)测试套件。