Unit Test Private Methods in Java – Java中的单元测试私有方法

最后修改: 2022年 6月 10日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll briefly explain why testing private methods directly is generally not a good idea. Then we’ll demonstrate how to test private methods in Java if it’s necessary.

在本教程中,我们将简要地解释为什么直接测试私有方法通常不是一个好主意。然后我们将演示如何在必要时测试Java中的私有方法。

2. Why We Shouldn’t Test Private Methods

2.为什么我们不应该测试私有方法

As a rule, the unit tests we write should only check our public methods contracts. Private methods are implementation details that the callers of our public methods aren’t aware of. Furthermore, changing our implementation details shouldn’t lead us to change our tests.

作为一项规则,我们编写的单元测试应该只检查我们的公共方法合同。私有方法是我们公共方法的调用者不知道的实现细节。此外,改变我们的实现细节不应该导致我们改变我们的测试。

Generally speaking, urging to test a private method highlights one of the following problems:

一般来说,敦促测试一个私有方法突出了以下问题之一。

  • We have dead code in our private method.
  • Our private method is too complex and should belong to another class.
  • Our method wasn’t meant to be private in the first place.

Hence, when we feel like we need to test a private method, what we should really do is fix the underlying design problem instead.

因此,当我们觉得需要测试一个私有方法时,我们真正应该做的是解决潜在的设计问题。

3. An Example: Remove Dead Code From a Private Method

3.一个例子 从一个私有方法中删除死代码

Let’s showcase a quick example of that.

让我们来展示一个快速的例子。

We’re going to write a private method that will return the double of an Integer. For null values, we want to return null:

我们要写一个私有方法,它将返回Integer的双数。对于null值,我们要返回null

private static Integer doubleInteger(Integer input) {
    if (input == null) {
        return null;
    }
    return 2 * input;
}

Now, let’s write our public method. It’ll be the only entry point from outside the class.

现在,让我们来写我们的公共方法。它将是类外的唯一入口。

This method receives an Integer as an input. It validates that this Integer isn’t null; otherwise, it throws an IllegalArgumentException. After that, it calls the private method to return twice the Integer‘s value:

该方法接收一个Integer作为输入。它验证这个Integer是不是null;否则,它会抛出一个IllegalArgumentException。之后,它调用私有方法来返回两倍的Integer的值。

public static Integer validateAndDouble(Integer input) {
    if (input == null) {
        throw new IllegalArgumentException("input should not be null");
    }
    return doubleInteger(input);
}

Let’s follow our good practice and test our public method contract.

让我们遵循我们的良好做法,测试我们的公共方法契约。

First, let’s write a test that ensures that an IllegalArgumentException is thrown if the input is null:

首先,让我们写一个测试,确保在输入为null时抛出IllegalArgumentException

@Test
void givenNull_WhenValidateAndDouble_ThenThrows() {
    assertThrows(IllegalArgumentException.class, () -> validateAndDouble(null));
}

Now let’s check that a non-null Integer is correctly doubled:

现在让我们检查一下,一个非空的Integer是否被正确地加倍了。

@Test
void givenANonNullInteger_WhenValidateAndDouble_ThenDoublesIt() {
    assertEquals(4, validateAndDouble(2));
}

Let’s have a look at the coverage reported by the JaCoCo plugin:

让我们看看JaCoCo插件报告的覆盖率

Code coverage of our methodsAs we can see, the null check inside our private method isn’t covered by our unit tests. Should we test it then?

Code coverage of our methods我们可以看到,单元测试没有覆盖我们的私有方法中的空检查。那我们是否应该测试它呢?

The answer is no. It’s important to understand that our private method doesn’t exist in a vacuum. It’ll only be called after the data is validated in our public method. Thus, the null check in our private method will never be reached; it’s dead code and should be removed.

答案是否定的。重要的是要明白,我们的私有方法并不存在于真空中。只有在我们的公共方法中验证了数据后,它才会被调用。因此,我们的私有方法中的空值检查将永远不会到达;它是死代码,应该被删除。

4. How to Test Private Methods in Java

4.如何测试Java中的私有方法

Assuming we’re not discouraged, let’s explain how to test our private method concretely.

假设我们不灰心,让我们解释一下如何具体地测试我们的私有方法。

To test it, it would be helpful if our private method had another visibility. The good news is that we’ll be able to simulate that with reflection.

为了测试它,如果我们的私有方法有另一个可见性,那将会很有帮助。好消息是,我们将能够用reflection来模拟这一点。

Our encapsulating class is called Utils. The idea is to access the private method called doubleInteger, which accepts an Integer as a parameter. Then we’ll modify its visibility to be accessible from outside the Utils class. Let’s see how we can do that:

我们的封装类被称为Utils。我们的想法是访问名为doubleInteger的私有方法,它接受一个Integer作为参数。然后我们要修改它的可见性,使其可以从Utils类之外访问。让我们来看看我们如何做到这一点。

private Method getDoubleIntegerMethod() throws NoSuchMethodException {
    Method method = Utils.class.getDeclaredMethod("doubleInteger", Integer.class);
    method.setAccessible(true);
    return method;
}

Now we’re able to use this method. Let’s write a test to ensure that, given a null object, our private method returns null. We’ll need to apply the method to a parameter that will be null:

现在我们能够使用这个方法了。让我们写一个测试,确保在给定一个null对象时,我们的私有方法返回null。我们需要将该方法应用于一个将是null的参数。

@Test
void givenNull_WhenDoubleInteger_ThenNull() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
    assertEquals(null, getDoubleIntegerMethod().invoke(null, new Integer[] { null }));
}

Let’s explain a bit more about the usage of the invoke method. The first argument is the object on which we apply the method. As doubleInteger is static, we passed in a null. The second argument is an array of parameters. In this case, we had only one parameter, and it was null.

让我们再解释一下invoke方法的用法。第一个参数是我们应用该方法的对象。由于doubleInteger是静态的,我们传入一个null。第二个参数是一个参数数组。在这个例子中,我们只有一个参数,而且是null

Finally, let’s demonstrate how we can also test the case of a non-null input:

最后,让我们演示一下如何测试非空输入的情况。

@Test
void givenANonNullInteger_WhenDoubleInteger_ThenDoubleIt() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    assertEquals(74, getDoubleIntegerMethod().invoke(null, 37));
}

5. Conclusion

5.总结

In this article, we learned why testing private methods is generally not a good idea. Then we demonstrated how to use reflection to test a private method in Java.

在这篇文章中,我们了解到为什么测试私有方法通常不是一个好主意。然后我们演示了如何使用反射来测试Java中的一个私有方法。

As always, the code is available over on GitHub.

像往常一样,代码可在GitHub上获得