如何在没有(非默认)构造函数的 Java class 中模拟对象?

How do I mock objects in a Java class that has no (non-default) constructor?

我正在为遗留 Tomcat Web 服务设置第一个单元测试,该服务在编写时并未考虑测试,也不使用 Spring。我遇到的 classes 之一是扩展 HttpServlet 的 servlet。这是 class.

的缩写版本
public class ItemServlet extends HttpServlet {
    private ObjectMapper mapper;
    private IItemDAO dao;

    @Override
    public void init() {
        mapper = new ObjectMapper();
        dao = new GenericItemDao(...);
    }
}

通常我会将外部依赖项传递给构造函数,但 servlet 是由 Tomcat 基于 web.xml 配置创建的,它仅调用默认构造函数和无参数 init()方法。结果,似乎没有任何方法可以进行依赖注入以允许我在单元测试中进行模拟。我能想到的唯一方法是创建一个仅用于测试的构造函数,我可以用它来实例化我的单元测试中的 class,并将 init() 方法保留为实际应用程序的方式称呼。我还可以创建第三个方法,它可以被构造函数和 init() 调用,如下所示:

public class ItemServlet extends HttpServlet {
    private ObjectMapper mapper;
    private IItemDAO dao;

    public ItemServlet(ObjectMapper mapper, IItemDAO dao) {
        initDependencies(mapper, dao);
    }

    private void initDependencies(ObjectMapper mapper, IItemDAO dao) {
        this.mapper = mapper;
        this.dao = dao;
    }

    @Override
    public void init() {
        initDependencies(new ObjectMapper(), new GenericItemDAO(...));
    }
}

是否有更简洁的方法来对这些 class 进行单元测试?

您可以将 getters/setters 添加到 class 并通过那里将 mocks 注入到 class。

我会利用这个机会解耦代码并使其可测试。事实上,如果您代表 Tomcat 实例化一个 servlet,只是为了检查其方法之一 returns 是否为预期值,您就超出了单元测试的范围,而是做集成测试。

使用 Mock 框架来解决这个问题会使当前的实现一成不变并且无法更改 - 除非这些更改与测试的更改一起进行。

我是 unit-testing 的绝对拥护者,而且我也是一个实用主义者,如果我发现自己处于难以测试的几个框架层中,我会尽一切可能。在这种情况下(我只知道你的 servlet 依赖性,不知道任何其他 - 你会知道你在哪里)我喜欢将我的代码分成

  • 原子,unit-testable 没有框架依赖性的单元
  • 连接代码,将可测试代码与框架连接起来。

接线代码自然不是很复杂,也没有得到任何测试(由于大量的依赖项)。不过它得到 peer-reviewed。