【问题标题】:Designing Java unit test with static calls inside method使用方法内部的静态调用设计 Java 单元测试
【发布时间】:2017-12-15 21:11:25
【问题描述】:

我正在尝试为一个类创建一个单元测试,该类的方法使用 apache 的 fluent-hc 库 (http://hc.apache.org/httpcomponents-client-ga/fluent-hc/apidocs/org/apache/http/client/fluent/Request.html) 中的 Request.Post 方法。

问题是我不希望它在每次运行测试套件时都发送实际请求,如果它是单元测试而不是集成测试则更是如此。但我不知道如何解决这个问题。

我是测试领域的新手,我读到的大部分时间都无法测试某些东西,这是课程设计的问题。

public class HttpRequestSenderServiceImpl implements RequestSenderService {

@Override
public Response sendRequest(String address, String messageBody) throws IOException {
    Request request = Request.Post(address).bodyString(messageBody, ContentType.APPLICATION_JSON);
    Response response = request.execute();

    return response;
}

}

我使用 Spring 框架和 Spring-Test、Mockito 和 TestNG 作为测试工具。如果你至少能指出我正确的方向,我会非常感激,任何要阅读的材料,书籍,视频,任何东西。我只是想学习以正确的方式去做。

我找到了一些“解决方案”来解决我的问题,但它们都使用 PowerMockito,而且我所读到的内容对你不利,因为它允许模拟静态方法、构造函数等,这会转化为糟糕的编码实践,即我在这里试图避免的事情。

【问题讨论】:

  • 所以你想模拟一个静态方法,对吧?
  • 不完全是,我想知道的是,我如何以不需要的方式设计该类。比如,如果是你需要打电话,你会如何设计它?
  • 我不认为,模拟一个静态方法是不好的。但是,如果您真的想避免,这是要测试的方法吗?我认为,这种方法不值得进行任何单元测试,因为你内部没有任何业务逻辑,你只是“伪装”了一个 spring post call。在我看来,即使是这种方法也需要安装集成测试。
  • 我也认为,你有一个很好的 sw 架构 - 框架 post 方法的调用在 RequestSenderService 内部,并且你所有的业务类都使用这个类,因此是松散耦合的。对于他们,你应该编写单元测试来测试他们的逻辑并模拟RequestSenderService ,但不是为了这个“包装器”。
  • 在单元测试中,您“期望”某些行为以及该方法在特定输入处的某些结果(或异常)。但是你会在这里测试什么?在这种情况下,您没有使用“真正的”框架(没有集成测试),结果总是只是一些 Response 模拟,但在您的测试方法中没有发生任何事情。

标签: java spring unit-testing mockito testng


【解决方案1】:

出于您的目的,问题更多在于 apache 的 fluent-hc Request.Post,它是静态的,而不是模拟友好的,而不是您的代码。

你可以使用Executor和mock execute方法,或者如果你想使用Request.Post,将它包装在另一个低级服务接口中并使用它:

interface RequestSender {
      Response send(String addr,msgBody)
}

class RequestSenderImpl implements RequestSender {
    public Response send(String addr,msgBody) {
       Request request = Request.Post(address).bodyString(messageBody,ContentType.APPLICATION_JSON);
       Response response = request.execute();
       return response;
    }
}

这样即使没有 mockito,您也可以轻松地模拟代码。

【讨论】:

  • 感谢您的快速回复。抱歉,我不能说得更具体,我只是在努力学习并正确地学习它,并且通篇阅读,不得不使用 PowerMockito 是不良代码实践的一种症状,这让我有点失望,因为我就是这样。从一开始就一直在努力避免。至于为什么,我的目标只是尽可能多地覆盖,我仍然要判断我是否应该测试一些东西。非常感谢您的时间和回答,非常感谢。
【解决方案2】:

@lujop 为提到的问题提供了一个建议,我认为这是解决问题的一种方法。

然而,关于使用模拟框架的早先声明,我并不认为它一直都是一种不好的做法。考虑@lujop 的建议,您可能最终会使用Test Double 对RequestSenderImpl 及其业务逻辑进行单元测试。

但是会有一个类,比如 - FluentHCRequestSender - 负责实际调用 Request.Post。现在,如果您正在为所有类编写 UT,则必须使用模拟框架来测试 FluentHCRequestSender 以确保该类使用正确的 API。

有一个地方可以使用模拟框架进行测试。但一般来说,如果您的依赖项是您、您的团队或您的组织创建的类,那么如果您最终使用了模拟框架,那么它只是指向一个糟糕的设计。

为了回答您的资源问题,这些资源可以帮助您编写更好的 UT 和一般代码,

SOLID principles - 这可能是永远在后面的一件事 我在写代码时的想法。

Working Effectively with Legacy Code 对我很有帮助。

Test Driven Development - 在我练习的过程中,不太容易 严格遵守你的日常生活,但这至少迫使我 不要推迟我的 UT 并与我的实际工作同时进行 类(如果不是之前)。

Design Patterns - 抓住一本好的设计模式书/视频 如果您可以访问图书馆或购买图书馆。这些概念可能看起来 令人生畏,但我发现其中一些在我的日常生活中非常有用 工作,尤其是在编写 UT 时。

但就个人而言,这是学习查看代码库中现有代码并了解哪些方面的优点和可以改进的方面的最佳方式。我发现查找和理解由优秀设计人员编写的代码比任何书籍都更有用。

希望这篇长篇大论有所帮助!!

PS - 感谢您尝试正确的方式。有时它不常说,虽然它应该是。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-12-24
    • 2019-05-11
    • 1970-01-01
    • 1970-01-01
    • 2017-02-16
    • 2016-06-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多