Answers:
我的0.02 $ ...这有点主观,因此请加一点盐,但希望它能使您思考和/或引发一些对话:
对我来说,单元测试的主要目的是确保您编写的代码能够满足合同规定的要求,并能使您的代码能够满足实际情况。有了单元测试,您可以更好地确保当您(或将来的其他人)重构代码时,如果您具有适当的状态覆盖范围,那么代码的任何外部使用者都应保持不受影响。(至少在您不希望它们受到影响的程度上)。
在大多数情况下,您应该能够编写既可以交付生产又可以进行单元测试的代码。一个好的开始可能是研究依赖注入模式和框架。(或您选择的语言/平台的其他哲学)。
外部实现可能会影响您的代码是正确的。但是,确保您的代码作为大型系统的一部分正确运行是集成测试的功能。(这也可以通过不同程度的努力实现自动化)。
理想情况下,您的代码应仅依赖于任何第三方组件的API合同,这意味着只要您的模拟满足正确的API,单元测试就仍然可以提供价值。
那就是说,我很容易承认,有时候我会放弃单元测试而只支持集成测试,但这只是在我的代码必须与文档较差的API与第三方组件进行如此大量交互的情况下。(即例外而不是规则)。
好吧,请注意存根和模拟之间的区别。不受代码中任何更改影响的是存根(stub)的典型行为,而不是模拟。让我们从模拟的定义开始:
模拟对象在测试条件下替换了真实对象,并允许在系统或单元测试中针对自身验证调用(交互)。
-单元测试的艺术
基本上,您的模拟应该检查交互的必要行为。因此,如果重构后与数据库的交互失败,则使用模拟的测试也将失败。这当然是有局限性的,但是通过精心计划,您的模拟将不仅仅是“坐在那里”,而且不会“破坏单元测试的目的”。
提出一个好的问题绝不是愚蠢的。
我会解决您的问题。
编写测试时,有两个主要选项可帮助确保测试正确:
这是一个例子:
TEST(MyTest, TwoPlusTwoIsFour) {
ASSERT_EQ(4, 2+2);
}
TEST(MyTest, TwoPlusThreeIsntFour) {
ASSERT_NE(4, 2+3);
}
最重要的是,如果你是单元测试的逻辑里面你的代码(而不是第三方库),那么它是完全正常的,你不用担心其他的密码破译,而在这方面。本质上,您正在测试逻辑包装和使用第三方工具的方式,这是经典的测试方案。
在课程级别完成测试后,就可以继续测试您的课程(根据我的理解,中介者)与第三方库之间的集成。这些测试称为集成测试,不使用模拟,而是使用所有类的具体实现。它们速度稍慢,但仍然非常重要!
听起来您好像有一个单片应用程序,它可以完成void main()
从数据库访问到输出生成的所有工作。在开始正确的单元测试之前,这里有几个步骤。
1)查找一段已编写/复制粘贴多次的代码。即使只是string fullName = firstName + " " + lastName
。将其分解为一个方法,例如:
private static string GetFullName (firstName, lastName)
{
return firstName + " " + lastName;
}
现在,您有一段可单元测试的代码,无论多么琐碎。为此编写一个单元测试。冲洗并重复此过程。最终您将获得大量逻辑分组的方法,并且能够从中提取许多类。这些类中的大多数将是可测试的。
另外,一旦提取了多个类,就可以从它们中提取接口并更新程序以与接口而不是对象本身对话。到那时,您可以使用模拟/存根框架(甚至是您自己手工制作的假东西),而根本无需更改程序。一旦将数据库查询提取到一个(或多个)类中,这将非常方便,因为现在您可以伪造查询应返回的数据。