Java TDD:测试用例的适用性

Java TDD:测试用例的适用性,java,junit,tdd,Java,Junit,Tdd,我从TDD和JUnit开始。在查看教程和文档后,我有一些问题,如果能得到一些最佳实践反馈,我会很高兴 A)我看到的所有示例都是针对具有某种语义/逻辑的方法 输入->逻辑->输出 e、 g.2数字->添加它们->结果 由于输入到输出的转换,testcase检查逻辑。 我理解这一点,这很好 如果没有这样的输入(或者对外部结果有很大的依赖),该怎么办 e、 g 我看不到真正的逻辑可以在编译/部署时独立于上下文进行检查 什么样的断言是有意义的,或者说,没有测试的样本是公平的 我认为只有测试上下文无关的输

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

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

输入->逻辑->输出

e、 g.2数字->添加它们->结果

由于输入到输出的转换,testcase检查逻辑。 我理解这一点,这很好

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

e、 g

我看不到真正的逻辑可以在编译/部署时独立于上下文进行检查

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

我认为只有测试上下文无关的输入才有意义。外部数据库或webrequest的结果不是这样(我想-你同意吗?)


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

首先,虽然大多数函数都可以进行测试,但并非所有函数都需要直接测试。有些可以通过调用代码的测试进行更好的测试

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

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


最后,您所谈论的“比率”,或者通常所称的“测试覆盖率”,取决于您对测试套件的信心程度。最后,正是这种自信让你能够重构代码,而不必担心代码会被破坏,这也是关键所在。

正如deHaar在评论中指出的那样,你需要涵盖很多边缘案例。测试db时,可以执行以下操作:

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

要记住一件事:您负责确保mock(或H2)的行为与您的真实DB相同@Kraylog建议进行集成测试,以将适配器写入IO设备,并进行契约测试,以确保模拟的行为方式相同。

在将测试主体与其常规环境隔离的测试中,您将经常看到
输入->逻辑->输出
模式,因为输入数据必须由环境提供,而测试是受试者体验的环境

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
这是相同的图案,只是雕刻的不同而已

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

这样的事情有时被称为

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

input->logic->output
logic
不一定是您的生产代码。编写与门面集成的测试是很常见的,门面协调测试主题与其(双重)依赖项之间的交互协议


(测试,就像生产代码一样,都有设计——现在投资一个好的设计可能会在测试的生命周期中产生巨大的利润)。

a:当您执行示例方法时会发生什么?可能没有具有给定id的工作人员值得测试(它是否
返回空值
或抛出
异常
?)。数据库连接可能存在问题(服务器脱机/关闭,客户端不联机…),应该对其进行测试(在这种情况下,您可以断言一些异常)。可能还有更多…使用手动测试来查看模拟的行为是否与真实的一样还不够好。我们使用集成测试将适配器写入IO设备,并使用契约测试确保模拟的行为方式相同。手动方式的问题是不能在构建中运行它。当事情发生变化时会发生什么?很好的洞察力!谢谢我会更正答案。谢谢你@Kraylog
// Copy input to database
// connect test subject to database
// invoke query, thereby retrieving the output
// Use the input to initialize the test double
// connect test subject to test double
// invoke query, thereby retrieving the output