如何在实现之前测试void方法?

时间:2018-06-02 22:20:21

标签: java unit-testing oop design-patterns tdd

我正在开发一个新项目。这是迄今为止所做的:

  1. 技术设计。
  2. 模型类(数据类)。
  3. 项目中的所有接口(但尚未实现)。
  4. 我想做的下一件事是从骨架(高级方法)到嵌套对象实现方法。不过,我想在编写实现之前为每个方法创建一个单元测试。首先实现高级方法不会有任何问题,因为我将使用接口并仅使用DI在外部Java配置文件中绑定具体实现。

    我要实现的第一种方法称为lookForChanges(),它接受​​并返回void。这个方法由Spring的调度程序(@Scheduled)调用,它管理整个过程:它从数据库中检索数据,从Web服务检索数据,比较它们,如果有任何更改,则更新数据库并将JMS消息发送到客户端。当然,它并不是自己做所有这些事情,而是调用相关的类和方法。

    所以我遇到的第一个问题是如何为void方法创建单元测试。在所有教程中,测试方法始终接受参数并返回结果。我找到了in this question的答案。他说即使没有检查结果,至少有一个可以确保调用测试方法中的方法并且参数的顺序正确。

    我有点喜欢这个答案,但问题是我正在使用TDD所以对于提出这个问题的人来说,我在实施测试方法之前写了测试,所以我不喜欢#39;不知道它将使用哪些方法和顺序。我可以猜测,但只有在方法已经实施后我才会确定。

    那么,在实现之前如何测试void skeleton方法呢?

1 个答案:

答案 0 :(得分:4)

  

所以我遇到的第一个问题是如何为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`
}

后者是我们可以测试的API - 我们配置协作者,将它们传递给被测系统,然后验证更改。

此时我们还没有对void copy()进行测试,但是没关系,因为代码很简单,很简单,显然没有任何缺陷&# 34。

请注意,从测试的角度来看,以下设计之间没有太大区别

{
    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,首先编写 test

// Arrange the test context to be in the correct initial state
// ???
// Verify that the test context arrived in final state consistent with the specification.

也就是说,在开始考虑API之前,首先需要弄清楚如何评估结果。

同样的想法,不同的拼写:如果函数调用的效果是不可检测的,那么你也可以只发出一个no-op。如果一个no-op不能满足你的要求,那么某处必须有一个可观察到的效果 - 你只需要弄清楚是直接观察到这种效应(检查一个返回值),还是通过代理(检查效果)解决方案中的其他一些元素,或者扮演该元素角色的测试双方。)

  

好的,现在我可以将params传递给测试它的方法,但是我可以测试什么?

你测试它应该做什么。

尝试这个思想实验 - 假设你和我配对,你提出了这个界面

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是单一的#34;单位"。

  

我必须首先看到foo的实现。我错了吗?

是的 - 您需要了解foo的规范,而不是实现。

相关问题