当前位置: 首页 > 知识库问答 >
问题:

有没有办法阻止Java JUnit测试的所有外部网络访问?

朱梓
2023-03-14

我经常从事Spring Boot Java应用程序(用Gradle或Maven构建)的工作,这些应用程序对外部服务进行网络调用(例如,对外部的类似REST的API)。当运行自动化(JUnit)测试时,我不希望这些调用发生,即使是偶然的。调用真实的endpoint(即使是为测试而设计的“开发”环境)可能会影响外部状态,导致不必要的服务器工作负载,或者无意中向endpoint泄露信息。它还可能导致测试无意中依赖于正在运行的外部资源,如果在构建项目时或者离线构建项目时资源停止运行,就会导致构建失败。

这些调用通常应该发生的情况是,它们要么被模拟为不做任何事情,要么应该指向同一台机器上的服务(例如,由测试启动的本地主机上的WireMockendpoint,或者由测试使用Testcontainers启动的本地Docker容器)。

在这些情况下,我倾向于创建一个特定于测试的bean定义配置文件,该配置文件使用明显虚假的东西覆盖所有HTTPendpoint,这些endpoint保证不会在某个地方路由,例如 http://example.invalid/bookSearch。这样,如果开发人员错过了在测试中模拟或更改endpoint,它就不会调用真实的endpoint并触发真正的副作用。

但是,这种方法可能容易出错,并且在实现中或未来更改中的疏忽可能会导致仍然进行网络调用。示例包括:

  1. 开发人员可以添加一个新的endpoint,而无需记住或知道他们需要覆盖测试路由
  2. 一个endpoint可能会在第一时间被覆盖
  3. 第三方库可能正在发出开发人员不知道或未说明的请求

这主要与启动部分或全部环境的Spring集成测试有关(例如,使用@SpringBootTest@ExantWith(SpringExtension.class))。然而,如果组件使用连接到网络的非必需构造函数参数,或者如果开发人员传入一个真正的网络连接组件,他们应该使用模拟或连接到localhost的东西,这可能适用于单元测试。

我突然想到,对于这种情况,可能有一种更为防弹的解决方案。例如,也许有一种方法可以告诉JVM或其底层的HTTP/FTP/etc。用于阻止所有外部网络流量的库。

有没有办法在Java Spring Boot JUnit测试中阻止非本地网络访问,或者保证外部网络endpoint不会被调用?

共有3个答案

柳钟展
2023-03-14

我不知道在JVM环境中如何做到这一点。

听起来你更多的是在谈论功能/集成测试,而不是单元测试。我怀疑docker可能会工作得很好,因为您可以显式隔离容器的网络。我个人使用WireMock和docker compose来做类似的事情。

越姚石
2023-03-14

将连接重定向到不存在的代理应该提供您要求的行为。

要使用的网络(系统)属性:

对于HTTP:

  • http.proxyHost
  • http.proxyPort

对于HTTPS:

    < li>https.proxyHost < li>https.proxyPort

对于 FTP:

  • ftp.proxyHost
  • ftp.proxyPort

SOCKS 代理设置可用于拦截所有 TCP 流量:

  • 袜子代理主机
  • 袜子代理端口

为上述任何 *主机属性指定本地主机将强制 JVM 尝试连接到运行构建的主机。只要没有进程在端口上侦听,连接尝试就会失败。SOCKS 端口 1080 对于上述任何 *端口属性看起来都很安全。

另一种选择是使用类似WireMock的东西在构建主机上打开一个端口,并将所有TCP流量重定向到那里,但在建立连接后立即丢弃WireMock端的连接。

对于基于 Maven 的构建系统 - 请查看 Maven Surefire 插件文档,了解如何为测试指定系统属性:

  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>3.0.0-M5</version>
    <configuration>
      <systemPropertyVariables>
        <socksProxyHost>localhost</socksProxyHost>
        <socksProxyPort>1080</socksProxyPort>
        [...]
      </systemPropertyVariables>
    </configuration>
  </plugin>
乜思淼
2023-03-14

您可以在测试中使用Sniffy禁用网络-请参阅此处的示例。

    @Rule public SniffyRule sniffyRule = new SniffyRule();

    @Test
    @DisableSockets
    public void testDisableSockets() throws IOException {
        try {
            new Socket("google.com", 443);
            fail("Sniffy should have thrown ConnectException");
        } catch (ConnectException e) {
            assertNotNull(e);
        }
    }

关于实现的一些见解:Sniffy安装了自定义SocketImFactory和其他网络原语,例如NIO SelectorProvider,以便注入延迟,甚至完全禁用与选定或所有主机的连接。

声明:我是《嗅嗅》的作者

 类似资料:
  • getExternalStorage()现在在4.4A中,指向模拟存储...由于基本的android手机没有外部SD卡的选项,而其他手机有该选项,有没有办法获取外部SD卡的路径? 首先非常感谢,

  • 在web上,我只找到了一种停止迭代方法的方法。通过使用limit()函数。但这会迭代一个具体的循环数。我想用它来停止一个谓词。 有没有办法用Streams做到这一点? 更新1:使用Java 8

  • 因为这个方法是在运行时执行的,所以需要对它进行测试吗

  • 我想使用azure密钥库来存储没有azure ad身份验证的控制台应用程序的连接字符串。 那么,没有Azure AD有没有办法访问Azure密钥库呢?

  • 问题内容: 我已经构建了一种通过$ http返回资源的复杂方法。 该方法返回一个Promise,然后检查资源是否存在我的本地缓存。如果这样做,它将返回缓存的资源,否则,将发出$ http请求。在资源被缓存后,这种方法效果很好,但是在整个应用程序中,我有多种功能正在按此方法加载,并且每个函数都会发出http请求,因为资源尚未返回和缓存。 我想出了一个简单的检查方法来解决此问题,但我觉得应该有一个更好

  • 问题内容: 我需要创建一个伪的帮助程序类以用于单元测试(注入到测试的类中)。有没有办法在此类中使用TestCase断言? 我想将这些断言用于Fake类执行的一些常见检查。就像是: 问题答案: 您可以创建的实例并在其上调用方法。 在较旧的Python版本(Python 2.7和更早的版本3.0、3.1)上,您需要在类类中传递 现有 方法的名称(通常在子类上传递测试方法的名称)。在这种情况下将: 但是