gpt4 book ai didi

java - JUnit 更改集成测试中的类或方法行为

转载 作者:行者123 更新时间:2023-12-01 21:47:58 25 4
gpt4 key购买 nike

我有一个 java 应用程序(内部没有 Spring),我想通过集成测试来测试它。

我的主要用例是主函数,它使用指定的输入在数据库上执行一些操作并向两个不同的服务(一个 SOAP 和一个 REST)发送一些请求。

现在我有一个有效的 JUnit 配置(分为单元测试和集成测试)+ io.fabric8:docker-maven-plugin,它在集成测试期间使用 docker 镜像作为数据库。

我想做的是为这两个服务添加模拟,特别是用于直接调用外部服务的方法。

最大的问题是我有这样的结构:

class A{
Result mainFunction(Request r){
....
B b = new B(params);
b.logEvent(someParameters)
....
}
}
class B{
int logEvent(Object someParameters){
....
NotifierHandler nh = new NotifierHandler(param1);
nh.sendNotification(json);
....
}
}

我有:

class NotifierHandler{
String sendNotification(Json j){
...
[call to REST service with some parameters]
...
...
[call to SOAP service with some parameters]
...
}
}

我需要什么:调用 A.mainFunction(r) 在测试环境中,用 FakeNotifierHandler 替换 NotifierHandler 和/或更改方法的行为sendNotification()

实际问题:现在使用 Mockito 和 PowerMock 时遇到的问题是无法使用 FakeNotifierHandler 全局直接更改类 NotifierHandler。同样尝试改变方法的行为。

特别是,我需要创建一个

class FakeNotifierHandler{
String sendNotification(Json j){
...
[save on an HashMap what I should send to the REST service]
...
...
[save on another HashMap what I should send to the SOAP service]
...
}
}

阅读我尝试过的所有示例,我只看到了更改方法返回值的简单示例,而不是更改一个类的一个方法的行为,该方法被另一个类和另一个我用作集成测试起点的类所使用.

注意:可能有一种快速的方法可以做到这一点,但我对这种类型的测试(Mockito,PowerMock,...)非常陌生,并且我没有找到这种特殊奇怪情况的示例。

编辑:与 How to mock constructor with PowerMockito 不相似因为我需要改变方法的行为,而不仅仅是返回值。

提前非常感谢

最佳答案

我找到了一个非常有效且非常简单的解决方案!

解决方案是 PowerMock ( https://github.com/powermock/powermock ),特别是用另一个类实例替换类实例的创建: https://github.com/powermock/powermock/wiki/mockito#how-to-mock-construction-of-new-objects

我的项目中只有一个问题,那就是 JUnit 5。PowerMock 支持 JUnit 4,因此,仅在解决方案的某些测试中使用它。为了做到这一点,需要替换

import org.junit.jupiter.api.Test;

import org.junit.Test;

为了使用“whenNew()”方法,我扩展了测试中必须替换的类,并且仅覆盖了集成测试所需的方法。此解决方案的最大好处是我的代码未受影响,并且我也可以在旧代码上使用此方法,而无需在代码重构期间引入回归的风险。

关于集成测试的代码,这里有一个例子:

import org.junit.jupiter.api.DisplayName;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PowerMockIgnore({"javax.crypto.*" }) // https://github.com/powermock/powermock/issues/294
@PrepareForTest(LegacyCoreNetworkClassPlg.class) // it is the class that contains the "new SOAPCallHelper(..)" code that I want to intercept and replace with a stub
public class ITestExample extends InitTestSuite {
@Test
@DisplayName("Test the update of a document status")
public void iTestStubLegacyNetworkCall() throws Exception {

// I'm using JUnit 4
// I need to call @BeforeAll defined in InitTestSuite.init();
// that works only with JUnit 5
init();

LOG.debug("IN stubbing...");
SOAPCallHelperStub stub = new SOAPCallHelperStub("empty");
PowerMockito.whenNew(SOAPCallHelper.class).withAnyArguments().thenReturn(stub);
LOG.debug("OUT stubbing!!!");

LOG.debug("IN iTestStubLegacyNetworkCall");
...
// Here I can create any instance of every class, but when an instance of
// LegacyCoreNetworkClassPlg.class is created directly or indirectly, PowerMock
// is checking it and when LegacyCoreNetworkClassPlg.class will create a new
// instance of SOAPCallHelper it will change it with the
// SOAPCallHelperStub instance.
...
LOG.debug("OUT iTestStubLegacyNetworkCall");
}
}

这里是pom.xml的配置

    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.jupiter.version>5.5.2</junit.jupiter.version>
<junit.vintage.version>5.5.2</junit.vintage.version>
<junit.platform.version>1.3.2</junit.platform.version>
<junit.platform.engine.version>1.5.2</junit.platform.engine.version>
<powermock.version>2.0.2</powermock.version>

<!-- FOR TEST -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!-- Only required to run tests in an IDE that bundles an older version -->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>${junit.platform.version}</version>
<scope>test</scope>
</dependency>
<!-- Only required to run tests in an IDE that bundles an older version -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<!-- Only required to run tests in an IDE that bundles an older version -->
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>${junit.vintage.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-engine</artifactId>
<version>${junit.platform.engine.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.vintage.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>

关于java - JUnit 更改集成测试中的类或方法行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58394005/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com