PowerMockito returns 模拟用于测试 public 方法的私有方法时为 null
PowerMockito returns null when mocking private method for testing public method
我正在尝试编写一个测试来比较字符串的相等性。
这是应该测试的 class 的代码片段
package ge.jibo.util;
public class TextManager {
private String concatenateTwoString(String t1, String t2) {
return t1 + t2;
}
public String concatenate(String t1, String t2) {
return concatenateTwoString(t1, t2);
}
}
这是一个测试class
package ge.jibo.util;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import static org.assertj.core.api.Assertions.assertThat;
public class TextManagerTest {
@Test
@PrepareForTest(TextManager.class)
void checkIfConcatenationWorks() throws Exception {
TextManager textmanager = PowerMockito.spy(new TextManager());
PowerMockito.doReturn("someText").when(textmanager,"concatenateTwoString", Mockito.anyString(), Mockito.anyString());
String text = textmanager.concatenate("ji","bo");
assertThat(text).isEqualTo("jibo");
}
}
如你所见,我想测试public方法concatenate
,它在同一个class中调用私有方法concatenateTwoString
。
我的想法是,我想为私有方法制作一个模拟对象,每当从 public 方法调用它时,它应该 return 常量值 "someText"
但它 return 是私有方法 concatenateTwoString
中的 null
而不是 "someText"
。
org.opentest4j.AssertionFailedError:
Expecting:
<null>
to be equal to:
<"jibo">
but was not.
Expected :jibo
Actual :null
有人知道怎么解决吗?
依赖版本:
- junit-木星 - 5.7.2
- junit 平台启动器 - 1.8.1
- 模拟核心 - 3.12.4
- mockito-junit-jupiter - 3.12.4
- powermock-核心 - 2.0.9
- powermock-api-mockito2 - 2.0.9
您忘记添加 @RunWith
并指定 PowerMockRunner
如果您能够使用 Junit-4,这就是解决方案。
@RunWith(PowerMockRunner.class)
@PrepareForTest(TextManager.class)
public class TextManagerTest {
@Test
public void checkIfConcatenationWorks() throws Exception {
// Arrange
TextManager textmanager = PowerMockito.spy(new TextManager());
PowerMockito.doReturn("someText").when(textmanager,"concatenateTwoString"
, Mockito.anyString()
, Mockito.anyString());
// Act
String text = textmanager.concatenate("ji","bo");
// Assert
assertThat(text).isEqualTo("jibo");
}
}
正如@heaprc
在他的post中提到的,“测试”方法可以正确执行,在我们使用JUnit4
而不是JUnit5
的情况下,通过为 powerMock @RunWith(PowerMockRunner.class)
.
添加 JUnit4
注释
一切正确,在JUnit5.
的情况下,没有直接模拟public下的私有方法的解决方案
那么我们有什么选择呢?
在m.o中,这种情况下有三个选项:
- 将
JUnit5
留给 "Test framework"
,不要尝试模拟私有方法。首先,想想你是否真的想测试那个 private
方法,如果有必要测试它 package-private
(通过将方法更改为 package-private
你正在打破封装的概念,但如果你真的需要测试它,那么我认为这不是一个大问题)
- 将您的
"Test framework"
从 JUnit5
降级到 JUnit4
。但在这种情况下,如果您已经在 JUnit5
中编写了 100 个测试,则必须更改所有测试才能在 JUnit4
中工作。必须降级框架的事实真的很烦人,而必须为 JUnit4
重写 100 个测试的事实非常烦人。
- 让
JUnit4
和 JUnit5
一起工作。我认为这是更好的解决方案,示例如下:
您的 pom.xml
文件中必须包含以下依赖项。
pom.xml
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.1</version>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.8.1</version>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
junit-vintage-engine
允许您 运行 JUnit4
测试。
junit-jupiter-engine
允许您 运行 JUnit5
测试。
代码片段
import org.junit.Assert;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@PrepareForTest(TextManager.class)
@RunWith(PowerMockRunner.class)
public class TextManagerTest {
@org.junit.Test //Test using Junit4
public void checkIfConcatenationWorksWithJunit4() throws Exception {
TextManager textmanager = PowerMockito.spy(new TextManager());
PowerMockito.doReturn("jibo").when(textmanager, "concatenateTwoString", Mockito.anyString(), Mockito.anyString());
String text = textmanager.concatenate("ji", "bo");
Assert.assertEquals(text,"jibo");
}
@Test //Test using Junit5
public void checkIfConcatenationWorksWithJunit5() {
Assertions.assertEquals("text","jibo");
}
}
在这种情况下,您将 运行 使用 Junit4
平台的 checkIfConcatenationWorksWithJunit4
测试方法,并且 @RunWith(PowerMockRunner.class)
将适用于此执行。
在 checkIfConcatenationWorksWithJunit5
方法的情况下,它将 运行 与 Junit5(Jupiter) 平台。
终于
您必须在 pom.xml
文件中添加 maven-surefire-plugin
才能使 JUnit4
和 JUnit5
测试在构建或使用 [=44= 测试时进行测试].
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
</plugins>
</build>
不添加 maven-surefire-plugin
唯一一个平台 (JUnit4)
测试将在 运行 时间执行。
我正在尝试编写一个测试来比较字符串的相等性。
这是应该测试的 class 的代码片段
package ge.jibo.util;
public class TextManager {
private String concatenateTwoString(String t1, String t2) {
return t1 + t2;
}
public String concatenate(String t1, String t2) {
return concatenateTwoString(t1, t2);
}
}
这是一个测试class
package ge.jibo.util;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import static org.assertj.core.api.Assertions.assertThat;
public class TextManagerTest {
@Test
@PrepareForTest(TextManager.class)
void checkIfConcatenationWorks() throws Exception {
TextManager textmanager = PowerMockito.spy(new TextManager());
PowerMockito.doReturn("someText").when(textmanager,"concatenateTwoString", Mockito.anyString(), Mockito.anyString());
String text = textmanager.concatenate("ji","bo");
assertThat(text).isEqualTo("jibo");
}
}
如你所见,我想测试public方法concatenate
,它在同一个class中调用私有方法concatenateTwoString
。
我的想法是,我想为私有方法制作一个模拟对象,每当从 public 方法调用它时,它应该 return 常量值 "someText"
但它 return 是私有方法 concatenateTwoString
中的 null
而不是 "someText"
。
org.opentest4j.AssertionFailedError:
Expecting:
<null>
to be equal to:
<"jibo">
but was not.
Expected :jibo
Actual :null
有人知道怎么解决吗?
依赖版本:
- junit-木星 - 5.7.2
- junit 平台启动器 - 1.8.1
- 模拟核心 - 3.12.4
- mockito-junit-jupiter - 3.12.4
- powermock-核心 - 2.0.9
- powermock-api-mockito2 - 2.0.9
您忘记添加 @RunWith
并指定 PowerMockRunner
如果您能够使用 Junit-4,这就是解决方案。
@RunWith(PowerMockRunner.class)
@PrepareForTest(TextManager.class)
public class TextManagerTest {
@Test
public void checkIfConcatenationWorks() throws Exception {
// Arrange
TextManager textmanager = PowerMockito.spy(new TextManager());
PowerMockito.doReturn("someText").when(textmanager,"concatenateTwoString"
, Mockito.anyString()
, Mockito.anyString());
// Act
String text = textmanager.concatenate("ji","bo");
// Assert
assertThat(text).isEqualTo("jibo");
}
}
正如@heaprc
在他的post中提到的,“测试”方法可以正确执行,在我们使用JUnit4
而不是JUnit5
的情况下,通过为 powerMock @RunWith(PowerMockRunner.class)
.
JUnit4
注释
一切正确,在JUnit5.
的情况下,没有直接模拟public下的私有方法的解决方案那么我们有什么选择呢?
在m.o中,这种情况下有三个选项:
- 将
JUnit5
留给"Test framework"
,不要尝试模拟私有方法。首先,想想你是否真的想测试那个private
方法,如果有必要测试它package-private
(通过将方法更改为package-private
你正在打破封装的概念,但如果你真的需要测试它,那么我认为这不是一个大问题) - 将您的
"Test framework"
从JUnit5
降级到JUnit4
。但在这种情况下,如果您已经在JUnit5
中编写了 100 个测试,则必须更改所有测试才能在JUnit4
中工作。必须降级框架的事实真的很烦人,而必须为JUnit4
重写 100 个测试的事实非常烦人。 - 让
JUnit4
和JUnit5
一起工作。我认为这是更好的解决方案,示例如下:
您的 pom.xml
文件中必须包含以下依赖项。
pom.xml
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.1</version>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.8.1</version>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
junit-vintage-engine
允许您 运行JUnit4
测试。junit-jupiter-engine
允许您 运行JUnit5
测试。
代码片段
import org.junit.Assert;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@PrepareForTest(TextManager.class)
@RunWith(PowerMockRunner.class)
public class TextManagerTest {
@org.junit.Test //Test using Junit4
public void checkIfConcatenationWorksWithJunit4() throws Exception {
TextManager textmanager = PowerMockito.spy(new TextManager());
PowerMockito.doReturn("jibo").when(textmanager, "concatenateTwoString", Mockito.anyString(), Mockito.anyString());
String text = textmanager.concatenate("ji", "bo");
Assert.assertEquals(text,"jibo");
}
@Test //Test using Junit5
public void checkIfConcatenationWorksWithJunit5() {
Assertions.assertEquals("text","jibo");
}
}
在这种情况下,您将 运行 使用 Junit4
平台的 checkIfConcatenationWorksWithJunit4
测试方法,并且 @RunWith(PowerMockRunner.class)
将适用于此执行。
在 checkIfConcatenationWorksWithJunit5
方法的情况下,它将 运行 与 Junit5(Jupiter) 平台。
终于
您必须在 pom.xml
文件中添加 maven-surefire-plugin
才能使 JUnit4
和 JUnit5
测试在构建或使用 [=44= 测试时进行测试].
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
</plugins>
</build>
不添加 maven-surefire-plugin
唯一一个平台 (JUnit4)
测试将在 运行 时间执行。