Java 如何在实现void方法之前对其进行测试?
我正在开发一个新项目。这是迄今为止所做的:Java 如何在实现void方法之前对其进行测试?,java,unit-testing,oop,design-patterns,tdd,Java,Unit Testing,Oop,Design Patterns,Tdd,我正在开发一个新项目。这是迄今为止所做的: 技术设计 模型类(数据类) 项目中的所有接口(但尚未实现) 接下来我要做的是实现从骨架(高级方法)到嵌套对象的方法。然而,在编写实现之前,我想为每个方法创建一个单元测试。首先实现高级方法不会有任何问题,因为我将使用接口并仅在使用DI的外部Java配置文件中绑定具体实现 我要实现的第一个方法叫做lookForChanges(),它接受并返回void。此方法由Spring的调度器(@Scheduled)调用,它管理整个过程:从DB检索数据,从web服务检索
lookForChanges()
,它接受并返回void。此方法由Spring的调度器(@Scheduled)调用,它管理整个过程:从DB检索数据,从web服务检索数据,比较它们,如果有任何更改,则更新数据库并向客户端发送JMS消息。当然,它本身并不做所有这些事情,但它调用相关的类和方法
所以我遇到的第一个问题是如何为void方法创建一个单元测试。在所有教程中,测试的方法始终接受参数并返回结果。我已经找到了答案。他说,即使没有结果要检查,至少有人可以确保被测试方法中的方法被调用,并且参数顺序正确
我有点喜欢这个答案,但问题是我在使用TDD,所以与问这个问题的人相反,我在实现测试方法之前编写测试,所以我还不知道它将使用哪些方法以及使用的顺序。我可以猜,但我只会确定,一旦该方法已经实现
那么,如何在实现一个void骨架方法之前测试它呢
所以我遇到的第一个问题是如何为void方法创建一个单元测试
void方法意味着合作者。你要核实一下
例如。假设我们需要一个将System.in
复制到System.out
的任务。我们将如何为此编写自动化测试
void copy() {
// Does something clever with System.in and System.out
}
但是如果你稍微眯一眼,你会发现你的代码看起来像
void copy() {
InputStream in = System.in;
PrintStream out = System.out;
// Does something clever with `in` and `out`
}
void copy() {
InputStream in = System.in;
PrintStream out = System.out;
copy(in, out);
}
void copy(InputStream in, PrintStream out) {
// Does something clever with `in` and `out`
}
如果我们在此基础上执行并提取方法重构,那么我们可能会得到如下代码
void copy() {
InputStream in = System.in;
PrintStream out = System.out;
// Does something clever with `in` and `out`
}
void copy() {
InputStream in = System.in;
PrintStream out = System.out;
copy(in, out);
}
void copy(InputStream in, PrintStream out) {
// Does something clever with `in` and `out`
}
后者是一个我们可以测试的API——我们配置协作者,将其传递给被测系统,然后验证更改
目前,我们没有对void copy()
进行测试,但这没关系,因为那里的代码“非常简单,显然没有缺陷”
请注意,从测试的角度来看,以下设计之间没有太大区别
{
Task task = new Task();
task.copy(in, out);
}
{
Task task = new Task(in, out);
task.copy();
}
{
Task task = Task.createTask();
task.copy(in, out)
}
{
Task task = Task.createTask(in, out);
task.copy();
}
对此的一种思考方式是:我们不是先编写API,而是先编写测试
也就是说,在开始考虑API之前,首先需要确定如何评估结果
相同的想法,不同的拼写:如果函数调用的效果不可检测,那么您也可以发送一个no-op。如果no-op不符合您的要求,那么一定有一个可观察的效果——您只需要确定该效果是直接观察到的(检查返回值),还是通过代理观察到的(检查对溶液中某些其他元素的影响,或扮演该元素角色的双重测试)
好的,现在我可以把参数传递给测试它的方法,但是我可以测试什么呢
你测试它应该做什么
试试这个思维实验——假设你和我是结对的,你提出了这个界面
interface Task {
void lookForChanges();
}
然后,经过深思熟虑,我实现了这一点:
class NoOpTask implements Task {
@Override
void lookForChanges() {}
}
您如何证明我的实现不满足要求
您在该问题中所写的是“它更新数据库并向客户机发送一个JMS消息”,因此有两个断言要考虑——数据库是否被更新,并且是一个JMS消息发送? 整个事情看起来像这样
Given:
A database with data `A`
A webservice with data `B`
A JMS client with no messages
When:
The task is connected to this database, webservice, and JMS client
and the task is run
Then:
The database is updated with data `B`
The JMS client has a message.
看起来你建议的是端到端测试
它看起来确实像一个,但是如果你对这些合作者使用测试双工,而不是活的系统,那么你的测试是在一个孤立的、确定性的shell中运行的
这可能是一个社交测试——测试不知道或不关心when子句中被测试系统的实现细节。我不认为SUT是一个“单元”
我必须先看看foo的实现,我错了吗
是-您需要了解foo的规范,而不是实现。该方法应该做什么?这决定了您应该测试什么。无效方法更难测试,因为您需要在测试代码中观察副作用,这被认为是一种反模式。那么,什么是
lookForChanges()
应该在被测类的状态下修改吗?@MickMnemonic我已经解释了它应该做什么:交互DB层以检索数据,交互Web服务层以检索数据,比较两个位置的数据,如果它们之间存在差异,则它交互DB层以更新DB,以及交互它是JMS层,用于向客户端发送通知。它不会更改对象状态(名为MainService)中的任何内容。这很公平。该方法基本上等同于main()
,为其编写单元测试当然是可能的,但不会增加太多价值,因为您主要是针对mock测试交互(DB、WS、JMS)。也许可以将数据比较提取到一个单独的、单元可测试的方法/类中?@MickMnemonic是的,当然,这个方法本身不会进行比较,但会调用另一个方法来进行比较