PowerMockito模拟私有方法测试公共方法时返回null

3

我想编写一个测试来比较字符串的相等性。

以下是应该被测试的类的代码片段:

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);
  }

}

下面是一个测试类:

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");
  }
}

如您所见,我想测试公共方法 concatenate,该方法在同一类中调用私有方法 concatenateTwoString

我的想法是为私有方法创建一个模拟对象,每当它从公共方法被调用时,它应该返回常量值"someText"

但是从私有方法 concatenateTwoString 返回的是 null 而不是 "someText"

org.opentest4j.AssertionFailedError: 
Expecting:
 <null>
to be equal to:
 <"jibo">
but was not.
Expected :jibo
Actual   :null

有人知道如何修复吗?
依赖版本:
  1. junit-jupiter - 5.7.2
  2. junit-platform-launcher - 1.8.1
  3. mockito-core - 3.12.4
  4. mockito-junit-jupiter - 3.12.4
  5. powermock-core - 2.0.9
  6. powermock-api-mockito2 - 2.0.9
2个回答

1
@heaprc 在他的帖子中提到的那样,当我们使用 JUnit4 替代 JUnit5 并为 powerMock 添加 JUnit4 注释 @RunWith(PowerMockRunner.class) 时,“Test” 方法可以正确执行。
一切都正确,对于 JUnit5 情况下,在公共方法下模拟私有方法没有直接的解决方案。
那么我们有哪些选择呢?
在这种情况下,m.o 有三个选项。
  1. JUnit5 作为 “测试框架”,不要试图模拟私有方法。首先,考虑一下您是否真的想测试那个 private 方法,如果需要测试,请将其更改为 package-private(通过更改方法为 package-private,您正在破坏封装的概念,但如果您确实需要测试它,那么我认为这不是一个大问题)。
  2. 将您的 “测试框架”JUnit5 降级到 JUnit4。但在这种情况下,如果您已经编写了 100 个 JUnit5 测试用例,则必须将它们全部更改为在 JUnit4 上运行。不得不降级框架真的很烦人,而且不得不为 JUnit4 重写 100 个测试用例非常恼人。
  3. 使 JUnit4JUnit5 协同工作。我认为这是更好的解决方案,以下是示例:

您必须在 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,以便在构建或使用maven命令进行测试时同时测试JUnit4和JUnit5测试。
   <build>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
            </plugin>
        </plugins>
    </build>

如果不添加maven-surefire-plugin插件,运行时只会执行一个平台(JUnit4)测试。


0
你忘记添加 @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");
  }
}

我添加了 @RunWith 注解,但是没有任何改变。 - JiboOne
如果我将“concatenateTwoString”方法从私有更改为包级私有,则它可以完美地工作。我不明白原因。 - JiboOne
RunWith 和 Jupiter 不兼容。 - johanneslink
@heaprc,您建议添加“@RunWith(PowerMockRunner.class)”注释适用于junit4,但是您知道如何在Junit5的情况下解决此问题吗? - JiboOne

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接