繁体   English   中英

Java 单元测试:从公共方法或 package 私有方法进行测试

[英]Java Unit Test: Test from public method or package private method

我有一个 class Price和一个公共方法calculate() ,它调用另一个私有方法calculateByA()

public class Price {
    public SomeObject calculate() {
        if(someCondition) {
            calculateByA()
            // ....
        } else {
            //Calculate something else
        }
    }

    private int calculateByA() {
        //calculation logic
    }
}

我想做一个单元测试来测试calculationByA()的逻辑。

我曾想过:

  1. 通过调用公共方法calculate()进行测试并将someCondition设置为true
  2. calculateByA()更改为 package-private 并在测试中直接调用它。

但是,我不确定哪种方法更好。

我非常不喜欢使用 powermock 等。 问题不在于它们让您绕过可见性系统(其他语言根本不需要,例如参见 python),而是您尝试测试实现细节。

为可见方法编写测试并观察其私有方法的副作用。 在您的示例中,例如在 someCondition 中测试,您的结果与这种情况下的预期相符。 如果您因为有很多私有方法而遇到问题,请考虑使用 package 并将您的代码拆分 - 无论如何它可能太大了。 也使用 DI 卢克!

您的问题有三个选项。 最终取决于您自己的情况。 但是,这里有一些关于如何在它们之间进行选择的想法:

  1. 通过公共接口测试方法

在您提到的情况下,您可以使用正确的配置调用calculate来执行calculateByA 我认为这是最好的方法。 代码的结构意味着calculate是访问calculateByA的公共接口。 这意味着当这种行为发生变化时,您的测试将提醒人们注意问题。 这反过来意味着 class 的用户将体验到行为上的变化。

  1. 将定义更改为受保护

正如您所提到的,这将允许您从同一 package 中的其他类(包括单元测试类)调用calculateByA 这无疑是一种允许对calculateByA进行简单且独立的单元测试的方法。 但是,我不喜欢这样,仅仅是因为您只希望通过calculate function 访问,并且通过更改访问权限,同一 package 中的其他类很容易直接调用calculateByA

  1. 使用反射直接调用私有方法

有许多技术允许您在测试中直接调用私有方法。 我不会重复@Tiemo Vorschütz 的出色回答,其中详细介绍了如何执行此操作。 然而,这将是我最后的手段。 简单的反射测试更加脆弱,并且增加了测试套件的维护成本。

PowerMock 让您可以直接测试您的私有方法:

价格.java:

import java.util.concurrent.ThreadLocalRandom;

public class Price {

  private final boolean someCondition;


  public Price(final boolean someCondition) {

    this.someCondition = someCondition;
  }


  public int calculate() {
    if (someCondition) {
      return calculateByA();
    } else {
      // Calculate something else
      return 0;
    }
  }


  private int calculateByA() {
    final int randomNum = ThreadLocalRandom.current().nextInt(0, 10 + 1);
    return 4711 * randomNum;
  }
}

PriceTest.java:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;

@RunWith(PowerMockRunner.class)
@PrepareForTest(Price.class)
public class PriceTest {

  /*
   * Refelection test for a single private method
   */
  @Test
  public void calculateByA() throws Exception {

    final Price instance = new Price(true);
    final int result = Whitebox.invokeMethod(instance, "calculateByA");
    System.out.println(result);
  }


  /*
   * Test public method calculate which invokes private method calculateByA
   */
  @Test
  public void testCalculate() throws Exception {

    final Price instance = new Price(true);
    System.out.println(instance.calculate());
  }
}

JUnit4 旁边的依赖项:

</properties>
     <powermock.version>2.0.2</powermock.version>
</properties>

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>2.28.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-core</artifactId>
    <version>${powermock.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>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM