我经常从事Spring Boot Java应用程序(用Gradle或Maven构建)的工作,这些应用程序对外部服务进行网络调用(例如,对外部的类似REST的API)。当运行自动化(JUnit)测试时,我不希望这些调用发生,即使是偶然的。调用真实的endpoint(即使是为测试而设计的“开发”环境)可能会影响外部状态,导致不必要的服务器工作负载,或者无意中向endpoint泄露信息。它还可能导致测试无意中依赖于正在运行的外部资源,如果在构建项目时或者离线构建项目时资源停止运行,就会导致构建失败。
这些调用通常应该发生的情况是,它们要么被模拟为不做任何事情,要么应该指向同一台机器上的服务(例如,由测试启动的本地主机上的WireMockendpoint,或者由测试使用Testcontainers启动的本地Docker容器)。
在这些情况下,我倾向于创建一个特定于测试的bean定义配置文件,该配置文件使用明显虚假的东西覆盖所有HTTPendpoint,这些endpoint保证不会在某个地方路由,例如 http://example.invalid/bookSearch
。这样,如果开发人员错过了在测试中模拟或更改endpoint,它就不会调用真实的endpoint并触发真正的副作用。
但是,这种方法可能容易出错,并且在实现中或未来更改中的疏忽可能会导致仍然进行网络调用。示例包括:
这主要与启动部分或全部环境的Spring集成测试有关(例如,使用@SpringBootTest
或@ExantWith(SpringExtension.class)
)。然而,如果组件使用连接到网络的非必需构造函数参数,或者如果开发人员传入一个真正的网络连接组件,他们应该使用模拟或连接到localhost
的东西,这可能适用于单元测试。
我突然想到,对于这种情况,可能有一种更为防弹的解决方案。例如,也许有一种方法可以告诉JVM或其底层的HTTP/FTP/etc。用于阻止所有外部网络流量的库。
有没有办法在Java Spring Boot JUnit测试中阻止非本地网络访问,或者保证外部网络endpoint不会被调用?
我不知道在JVM环境中如何做到这一点。
听起来你更多的是在谈论功能/集成测试,而不是单元测试。我怀疑docker可能会工作得很好,因为您可以显式隔离容器的网络。我个人使用WireMock和docker compose来做类似的事情。
将连接重定向到不存在的代理应该提供您要求的行为。
要使用的网络(系统)属性:
对于HTTP:
对于HTTPS:
对于 FTP:
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>
您可以在测试中使用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)上,您需要在类类中传递 现有 方法的名称(通常在子类上传递测试方法的名称)。在这种情况下将: 但是