TDD:测试用例的适用性

时间:2019-04-25 07:35:16

标签: java junit tdd

我从TDD和JUnit开始。在查看了教程和文档后,我有一些问题,如果能得到一些最佳实践反馈,我将很高兴。

A)我看到的所有示例都是针对具有某种语义/逻辑的方法的。

输入->逻辑->输出

例如2个数字->添加它们->结果

由于输入到输出的转换,测试用例检查逻辑。 我知道这一点,而且很好。

如果没有这样的输入(或者对外部结果有很大的依赖性)怎么办?

例如

String getName (int id)
{
  // read the name of a staffmember out of the DB and return it
} 

我看不到在编译/部署时可以检查上下文无关的真正逻辑。

什么断言是有意义的,或者是没有任何测试是公平的样本?

我认为只有针对上下文独立输入的测试才有意义。外部数据库或Web请求的结果不是(我认为-您同意吗?)。

B)你们认为“存在方法”与“带有测试用例的方法”的比率是多少?当然,这取决于项目或主题,但我会对一些数字感兴趣。

3 个答案:

答案 0 :(得分:3)

正如deHaar在他的评论中指出的那样,您需要介绍许多边缘案例。在测试数据库时,可以执行以下操作:

  • 您可以模拟数据库(在Spring项目的存储库中),并将其配置为返回/抛出。然后,在测试中,您将测试诸如givenNoCustomerInDB_thenNotFoundExceptionThrownIsWrappedToXXX()之类的东西。在这里,您将测试调用DB的服务方法是否捕获了异常并对其进行了相应的包装。对于这种方法,您可能需要查找Mockito,它是Java的“事实上的”模拟框架。
  • 另一种选择是拥有一个进行测试时使用的内存数据库(例如H2)。

要记住的一件事:您有责任确保模拟(或H2)的行为与您的真实数据库相同。 @Kraylog建议进行集成测试以将适配器写入IO设备,并进行合同测试以确保模拟行为的行为相同。

答案 1 :(得分:2)

首先,尽管大多数功能都可以进行测试,但并非所有功能都需要直接测试。通过对调用代码的测试可以更好地测试某些代码。

第二,当处理依赖状态的副作用或代码时,有一些方法可以创建测试特定场景所需的上下文。一种方法是使用test doubles

当然,我们需要对不是纯函数的代码进行测试。您可以将不是纯函数的代码量减到最少(例如,使用函数式编程),但是如果不是,则还需要对其余代码进行测试。

最后,您所谈论的“比率”或通常所说的“测试覆盖率”取决于您对测试套件的信心程度。最后,正是这种信心使您可以重构代码而不必担心破坏事物,最终,这才是重点。

答案 2 :(得分:1)

在将测试对象与正常环境隔离开的测试中,您将经常看到input -> logic -> output模式,因为输入数据必须由环境提供,并且测试是< / em>受试者所经历的环境。

TDD经常使用隔离测试;它们通常既快速又令人尴尬地并行,这意味着在设计阶段运行它们的机会成本较低。

String getName (int id)
{
    // read the name of a staffmember out of the DB and return it
} 

在这样的示例中,我们通常会转向“数据库”是可配置的设计,而在我们的测试中,我们将提供已预先加载到正确状态的内存数据库。

// Copy input to database
// connect test subject to database
// invoke query, thereby retrieving the output

是相同的图案,只是雕刻方式不同。

在很多情况下,我们可以将一些抽象的数据库引入我们的设计中,并使该抽象而不是数据库成为已配置的依赖项。因此,与其使用与数据库进行通信的抽象,不如使用更简单的实现,并对其进行硬编码以返回一些值。

这种东西有时被称为test double

// Use the input to initialize the test double
// connect test subject to test double
// invoke query, thereby retrieving the output

同样,出现相同的模式,“逻辑”的细节有所改变。

logic中的input -> logic -> output不一定是您的生产代码。通常会编写与立面集成的测试,以协调测试主题与其(双重)依赖关系之间的交互协议。

(测试就像生产代码一样具有设计-现在投资良好的设计可能会在测试的整个生命周期中产生可观的利润)。