📜 ⬆️ ⬇️

PowerMock (+ Mockito): a new look at unit testing

image
Quality code is impossible without tests. And quality tests - without mocks. Various useful libraries, such as EasyMock or Mockito, have long helped us in creating mocks. In my practice, I use the Mockito, as the most flexible, beautiful and functional tool. But, unfortunately, the Mockito also did not become a silver bullet. Restrictions have always been final classes, private fields and methods, static methods, and more. And you had to choose: either a beautiful design or a high-quality test cover. I, as an adherent of beautiful architecture and quality tests, did not suit such an alignment. And just recently I came across a wonderful library - PowerMock , which satisfied almost all of my requests. Except for one, but more on that later.


So let's get started. To work we need: knowledge of Java, JUnit, Mockito. All this good will be brewed in a simple Maven project (I hope this will surprise no one).
To begin with, make sure that the JUnit dependency of version 4 or higher is added to the project. Of course, everything can be configured and used with older versions. But we will do everything on the latest versions. Now add Mockito & PowerMock. It should be something like this:

<properties> <junit.version>4.11</junit.version> <mockito.version>1.9.5</mockito.version> <powermock.version>1.5</powermock.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId> <version>${mockito.version}</version> </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-mockito</artifactId> <version>${powermock.version}</version> <scope>test</scope> </dependency> </dependencies> 

')
Everything is ready to go. Getting started! I will not invent any task, ala "Hello World!" Or "Pet Clinic". Let us consider situationally on spherical examples. Those who have extensive experience writing tests will immediately see how this can be applied. And those who are just starting ... Everyone will understand when faced with similar situations in practice.

Let's start with the simple. Somewhere in the depths of our ingenious code the final class is used, the method call of which we need to check. Mockito is powerless, this class has no interface, and the class itself cannot have any heirs. We cannot change anything either, either because of architectural features, or because it is a third-party service. Code for clarity:

 //   public final class ExternalService { public void doMegaWork() { //   , //         =) } } //   public class InternalService { private final ExternalService externalService; public InternalService(final ExternalService externalService) { this.externalService = externalService; } public void doWork() { externalService.doMegaWork(); } } 


In order not to make a fuss, let us use the wonderful opportunity of PowerMock. The test will look like this:

 @RunWith(PowerMockRunner.class) @PrepareForTest({ ExternalService.class }) public class InternalServiceTest { private final ExternalService externalService = PowerMockito.mock(ExternalService.class); private final InternalService internalService = new InternalService(externalService); @Before public void before() { Mockito.reset(externalService); } @Test public void doWorkTest() { internalService.doWork(); Mockito.verify(externalService).doMegaWork(); } } 


Run the test - it works! We will understand what is what. The first thing you look at is the annotations @RunWith & @PrepareForTest. The first is needed to replace the standard JUnit test performer for PowerMock, which uses the magic of the classer, to solve the problem of creating a mock object from the final class. The second annotation tells the test performer what classes to prepare for the test. Next, we see that to create a mock object, we use the factor method from the PowerMockito set. That's all!

Another simple and interesting feature is to check calls to static methods. Listing:

 //   public class StaticService { public static void doStatic() { // } public static String doStaticWithParams(final Object obj) { return ""; } } //   public class UseStaticService { public String useStatic(final Object obj) { StaticService.doStatic(); // return StaticService.doStaticWithParams(obj); } } //    @RunWith(PowerMockRunner.class) @PrepareForTest({ StaticService.class }) public class UseStaticServiceTest { private static final Object OBJECT_PARAM = new Object(); private static final String RETURN_STRING = "result"; private final UseStaticService useStaticService = new UseStaticService(); public UseStaticServiceTest() { PowerMockito.mockStatic(StaticService.class); PowerMockito.when(StaticService.doStaticWithParams(OBJECT_PARAM)).thenReturn(RETURN_STRING); } @Test public void useStaticTest() { String result = useStaticService.useStatic(OBJECT_PARAM); PowerMockito.verifyStatic(); StaticService.doStatic(); PowerMockito.verifyStatic(); StaticService.doStaticWithParams(OBJECT_PARAM); assertEquals(RETURN_STRING, result); } } 


The @RunWith & @PrepareForTest annotations are also required for working with static methods. Consider why new instructions are needed:
PowerMockito.mockStatic (Class <?> Type) - creates a mock for all static methods in a given class. It is worth noting that you can create a mock only for the necessary methods. How - figure it out yourself;)
PowerMockito.when (T methodCall) .thenReturn (returnValue) is the standard way to set some behavior for the created stub.
PowerMockito.verifyStatic () - called before checking each static method call.
ExternalMegaService.doStatic () - determines which actual method should have been called.

Another great feature of PowerMock is the mock up of creating new objects. Consider this spherical example:

 // ,    public final class ExternalServiceFactory { public ExternalService createExternalService() { return new ExternalService(); } } //  ,        public class InternalService { private final ExternalServiceFactory externalServiceFactory; public InternalService(final ExternalServiceFactory externalServiceFactory) { this.externalServiceFactory = externalServiceFactory; } public void doWork() { externalServiceProvider.createExternalService.doMegaWork(); } } // , ,  @RunWith(PowerMockRunner.class) @PrepareForTest({ ExternalServiceFactory.class, ExternalService.class }) public class InternalServiceTest { private final ExternalService externalService = PowerMockito.mock(ExternalService.class); private final ExternalServiceFactory externalServiceFactory; private final InternalService internalService; public InternalServiceTest() throws Exception { PowerMockito.whenNew(ExternalService.class) .withNoArguments() .thenReturn(externalService); externalServiceFactory = new ExternalServiceFactory(); internalService = new InternalService(externalServiceFactory); } @Before public void before() { Mockito.reset(externalService); } @Test public void doWorkTest() { internalService.doWork(); Mockito.verify(externalService).doMegaWork(); } } 


The PowerMockito.whenNew (Class <?> Type) .withNoArguments (). ThenReturn (instance) construction tells PowerMock to replace the creation of type objects with an instance object in inspected classes. It is important that the object in which it is necessary to replace the creation of a mock object is created after this construction. It should also be noted that ExternalServiceFactory may not be a regular object, but a partial mock (spy) and then its behavior can also be checked.

An unpleasant fly in the ointment is that if you need to instruct a class (@PrepareForTest) that you are testing (for example, to initialize moki statics), then you will never know the extent of coverage of this class with tests, since coverage will not be able to inspect it. In such cases, I divide the test into two classes. In the first, I check everything that can be checked without instructing the class under test, in the second - only what you need to do @PrepareForTest.

These are some great testing features provided by PowerMock. He also has a lot of other features, such as mocking private methods, internal, nested, and anonymous classes, and much more. But the functionality described above is, in my opinion, vital. You can figure out the rest for yourself, or if you like my presentation, I can tell you in another article.

Source: https://habr.com/ru/post/172239/


All Articles