1. Overview
1.概述
Unit testing with the help of a mocking framework has been recognized as a useful practice for a long time, and the Mockito framework in particular has dominated this market in recent years.
在嘲讽框架的帮助下进行单元测试,长期以来一直被认为是一种有用的做法,特别是近年来,Mockito框架在这个市场上占据了主导地位。
And in order to facilitate decent code designs and make the public API simple, some desired features have been intentionally left out. In some cases, however, these shortcomings force testers to write cumbersome code just to make the creation of mocks feasible.
而为了便于进行体面的代码设计并使公共API简单化,一些需要的功能被有意地排除在外。然而,在某些情况下,这些缺陷迫使测试人员编写繁琐的代码,只是为了使创建模拟的工作可行。
This is where the PowerMock framework comes into play.
这就是PowerMock框架发挥作用的地方。。
PowerMockito is a PowerMock’s extension API to support Mockito. It provides capabilities to work with the Java Reflection API in a simple way to overcome the problems of Mockito, such as the lack of ability to mock final, static or private methods.
PowerMockito是PowerMock的扩展API,用于支持Mockito。它以一种简单的方式提供了与Java Reflection API协同工作的能力,以克服Mockito的问题,例如缺乏对final、static或private方法的模拟能力。
This tutorial will introduce the PowerMockito API and look at how it is applied in tests.
本教程将介绍PowerMockito API,并看看它是如何应用于测试的。
2. Preparing for Testing With PowerMockito
2.使用PowerMockito进行测试的准备工作
The first step to integrate PowerMock support for Mockito is to include the following two dependencies in the Maven POM file:
整合PowerMock对Mockito的支持的第一步是在Maven POM文件中包含以下两个依赖项。
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.6.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.6.4</version>
<scope>test</scope>
</dependency>
Next, we need to prepare our test cases for working with PowerMockito by applying the following two annotations:
接下来,我们需要通过应用以下两个注释来准备我们的测试案例,以便与PowerMockito一起工作。
@RunWith(PowerMockRunner.class)
@PrepareForTest(fullyQualifiedNames = "com.baeldung.powermockito.introduction.*")
The fullyQualifiedNames element in the @PrepareForTest annotation represents an array of fully qualified names of types we want to mock. In this case, we use a package name with a wildcard to tell PowerMockito to prepare all types within the com.baeldung.powermockito.introduction package for mocking.
@PrepareForTest注解中的fullyQualifiedNames元素代表了我们想要模拟的类型的完全合格名称数组。在这种情况下,我们使用带有通配符的包名来告诉PowerMockito为嘲讽准备com.baeldung.powermockito.introduction包内的所有类型。
Now we are ready to exploit the power of PowerMockito.
现在我们准备利用PowerMockito的力量。
3. Mocking Constructors and Final Methods
3.嘲弄构造函数和最终方法
In this section, we will demonstrate the ways to get a mock instance instead of a real one when instantiating a class with the new operator and then use that object to mock a final method.
在本节中,我们将演示在用new操作符实例化一个类时,如何获得一个模拟实例而不是一个真实的实例,然后使用该对象来模拟一个最终方法。
Here is how we’ll define the collaborating class, whose constructors and final methods will be mocked:
下面是我们如何定义协作类,其构造函数和最终方法将被模拟。
public class CollaboratorWithFinalMethods {
public final String helloMethod() {
return "Hello World!";
}
}
First, we create a mock object using the PowerMockito API:
首先,我们使用PowerMockito API创建一个模拟对象。
CollaboratorWithFinalMethods mock = mock(CollaboratorWithFinalMethods.class);
Next, we set an expectation saying that whenever the no-arg constructor of that class is invoked, a mock instance should be returned rather than a real one:
接下来,我们设置了一个期望值,即只要调用该类的无条件构造函数,就应该返回一个模拟实例,而不是一个真实实例。
whenNew(CollaboratorWithFinalMethods.class).withNoArguments().thenReturn(mock);
Let’s see how this construction mocking works in action by instantiating the CollaboratorWithFinalMethods class using its default constructor, and then we’ll verify the behaviors of PowerMock:
让我们通过使用默认的构造函数实例化CollaboratorWithFinalMethods类,来看看这种构造嘲弄是如何工作的,然后我们将验证PowerMock的行为。
CollaboratorWithFinalMethods collaborator = new CollaboratorWithFinalMethods();
verifyNew(CollaboratorWithFinalMethods.class).withNoArguments();
In the next step, an expectation is set to the final method:
在下一步,对最终的方法设置了一个期望值。
when(collaborator.helloMethod()).thenReturn("Hello Baeldung!");
This method is then executed:
然后执行这个方法。
String welcome = collaborator.helloMethod();
The following assertions confirm that the helloMethod method has been called on the collaborator object and returns the value set by the mocking expectation:
下面的断言确认helloMethod方法已经在collaborator对象上被调用,并返回由嘲讽期望设置的值。
Mockito.verify(collaborator).helloMethod();
assertEquals("Hello Baeldung!", welcome);
If we want to mock a specific final method rather than all the final ones inside an object, the Mockito.spy(T object) method may come in handy. This is illustrated in Section 5.
如果我们想模拟一个特定的最终方法,而不是一个对象中的所有最终方法,Mockito.spy(T object)方法可能会派上用场。这将在第5节中进行说明。
4. Mocking Static Methods
4.模拟静态方法
Suppose that we want to mock static methods of a class named CollaboratorWithStaticMethods.
假设我们想模拟一个名为CollaboratorWithStaticMethods的类的静态方法。
Here’s how we’ll declare this class:
下面是我们将如何声明这个类。
public class CollaboratorWithStaticMethods {
public static String firstMethod(String name) {
return "Hello " + name + " !";
}
public static String secondMethod() {
return "Hello no one!";
}
public static String thirdMethod() {
return "Hello no one again!";
}
}
In order to mock these static methods, we need to register the enclosing class with the PowerMockito API:
为了模拟这些静态方法,我们需要在PowerMockito API上注册包围的类。
mockStatic(CollaboratorWithStaticMethods.class);
Alternatively, we may use the Mockito.spy(Class<T> class) method to mock a specific one as demonstrated in the following section.
另外,我们可以使用Mockito.spy(Class<T>class)方法来模拟一个特定的,如下节所示。
Next, expectations can be set to define the values methods should return when invoked:
接下来,可以设置期望值来定义方法在被调用时应该返回的值。
when(CollaboratorWithStaticMethods.firstMethod(Mockito.anyString()))
.thenReturn("Hello Baeldung!");
when(CollaboratorWithStaticMethods.secondMethod()).thenReturn("Nothing special");
Or an exception may be set to be thrown when calling the thirdMethod method:
或者在调用thirdMethod方法时可以设置抛出一个异常。
doThrow(new RuntimeException()).when(CollaboratorWithStaticMethods.class);
CollaboratorWithStaticMethods.thirdMethod();
Now it’s time to run the first two methods:
现在是运行前两种方法的时候了。
String firstWelcome = CollaboratorWithStaticMethods.firstMethod("Whoever");
String secondWelcome = CollaboratorWithStaticMethods.firstMethod("Whatever");
Instead of calling members of the real class, the above invocations are delegated to the mock’s methods.
上述调用不是调用真实类的成员,而是委托给mock的方法。
These assertions prove that the mock has come into effect:
这些论断证明,模拟已经生效。
assertEquals("Hello Baeldung!", firstWelcome);
assertEquals("Hello Baeldung!", secondWelcome);
We are also able to verify behaviors of the mock’s methods, including how many times a method is invoked.
我们也能够验证模拟的方法的行为,包括一个方法被调用的次数。
In this case, the firstMethod has been called twice, while the secondMethod has never been called:
在这种情况下,firstMethod被调用了两次,而secondMethod从未被调用。
verifyStatic(Mockito.times(2));
CollaboratorWithStaticMethods.firstMethod(Mockito.anyString());
verifyStatic(Mockito.never());
CollaboratorWithStaticMethods.secondMethod();
Note: The verifyStatic method must be called right before any static method verification for PowerMockito to know that the successive method invocation is what needs to be verified.
注意:verifyStatic方法必须在任何静态方法验证之前调用,以便PowerMockito知道连续的方法调用是需要被验证的。
Lastly, the static thirdMethod method should throw a RuntimeException as declared on the mock before.
最后,静态的thirdMethod方法应该抛出一个RuntimeException,正如之前在mock上声明的那样。
It is validated by the expected element of the @Test annotation:
它由@Test注解的预期元素来验证。
@Test(expected = RuntimeException.class)
public void givenStaticMethods_whenUsingPowerMockito_thenCorrect() {
// other methods
CollaboratorWithStaticMethods.thirdMethod();
}
5. Partial Mocking
5.局部嘲弄
Instead of mocking an entire class, the PowerMockito API allows for mocking part of it using the spy method.
PowerMockito API允许使用spy方法来嘲弄整个类,而不是嘲弄整个类。
This class will be used as the collaborator to illustrate the PowerMock support for partial mocking:
这个类将被用作合作者来说明PowerMock对部分嘲弄的支持。
public class CollaboratorForPartialMocking {
public static String staticMethod() {
return "Hello Baeldung!";
}
public final String finalMethod() {
return "Hello Baeldung!";
}
private String privateMethod() {
return "Hello Baeldung!";
}
public String privateMethodCaller() {
return privateMethod() + " Welcome to the Java world.";
}
}
Let’s begin with mocking a static method, which is named staticMethod in the above class definition.
让我们从模拟一个静态方法开始,这个静态方法在上述类定义中被命名为staticMethod。
First, we use the PowerMockito API to partially mock the CollaboratorForPartialMocking class and set an expectation for its static method:
首先,我们使用PowerMockito API来部分模拟CollaboratorForPartialMocking类,并为其静态方法设置一个期望。
spy(CollaboratorForPartialMocking.class);
when(CollaboratorForPartialMocking.staticMethod()).thenReturn("I am a static mock method.");
The static method is then executed:
然后执行该静态方法。
returnValue = CollaboratorForPartialMocking.staticMethod();
The mocking behavior is verified:
嘲弄行为得到了验证。
verifyStatic();
CollaboratorForPartialMocking.staticMethod();
The following assertion confirms that the mock method has actually been called by comparing the return value against the expectation:
下面的断言通过比较返回值和期望值来确认模拟方法确实被调用了。
assertEquals("I am a static mock method.", returnValue);
Now it is time to move on to the final and private methods.
现在是时候进入最终方法和私有方法了。
In order to illustrate the partial mocking of these methods, we need to instantiate the class and tell the PowerMockito API to spy it:
为了说明这些方法的部分嘲弄,我们需要实例化这个类,并告诉PowerMockito API来spy它。
CollaboratorForPartialMocking collaborator = new CollaboratorForPartialMocking();
CollaboratorForPartialMocking mock = spy(collaborator);
The objects created above are used to demonstrate the mocking of both the final and private methods.
上面创建的对象被用来演示最终方法和私有方法的嘲弄。
We will deal with the final method now by setting an expectation and invoking the method:
我们现在将通过设置一个期望值并调用该方法来处理最后的方法。
when(mock.finalMethod()).thenReturn("I am a final mock method.");
returnValue = mock.finalMethod();
The behavior of partially mocking that method is proved:
部分嘲弄该方法的行为被证明。
Mockito.verify(mock).finalMethod();
A test verifies that calling the finalMethod method will return a value that matches the expectation:
一个测试验证了调用finalMethod方法将返回一个符合预期的值。
assertEquals("I am a final mock method.", returnValue);
A similar process is applied to the private method. The main difference is that we cannot directly invoke this method from the test case.
一个类似的过程也适用于私有方法。主要的区别是我们不能直接从测试用例中调用这个方法。
Basically, a private method is called by other ones from the same class. In the CollaboratorForPartialMocking class, the privateMethod method is invoked by the privateMethodCaller method, and we will use the latter as a delegate.
基本上,一个私有方法被同一个类中的其他方法所调用。在CollaboratorForPartialMocking类中,privateMethod方法被privateMethodCaller方法调用,我们将使用后者作为一个委托。
Let’s start with the expectation and invocation:
让我们从期望和调用开始。
when(mock, "privateMethod").thenReturn("I am a private mock method.");
returnValue = mock.privateMethodCaller();
The mocking of the private method is confirmed:
私有方法的嘲弄得到了确认。
verifyPrivate(mock).invoke("privateMethod");
The following test makes sure that the return value from invocation of the private method is the same as the expectation:
下面的测试确保了调用私有方法的返回值与期望值相同。
assertEquals("I am a private mock method. Welcome to the Java world.", returnValue);
6. Conclusion
6.结论
This article introduced the PowerMockito API, demonstrating its use in solving some of the problems developers encounter when using the Mockito framework.
本文介绍了PowerMockito API,展示了它在解决开发者使用Mockito框架时遇到的一些问题方面的用途。
The implementation of these examples and code snippets can be found in the linked GitHub project.
这些例子和代码片断的实现可以在链接的GitHub项目中找到。