Guide to Powermock in Java

Introduction

Powermock is a Java framework that extends other mock libraries such as EasyMock and Mockito to add more powerful capabilities, including the ability to mock static methods, constructors, and private methods. This guide will cover the installation, basic usage, advanced features, and various use cases of Powermock using the latest version.

Installation

Adding Powermock to Your Project

To use Powermock with Mockito, add the following dependencies to your pom.xml if you're using Maven:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>3.12.4</version> <!-- or the latest version -->
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>2.0.9</version> <!-- or the latest version -->
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito2</artifactId>
    <version>2.0.9</version> <!-- or the latest version -->
    <scope>test</scope>
</dependency>

For Gradle:

testImplementation 'org.mockito:mockito-core:3.12.4'
testImplementation 'org.powermock:powermock-module-junit4:2.0.9'
testImplementation 'org.powermock:powermock-api-mockito2:2.0.9'

Getting Started with Powermock

Mocking Static Methods

Static methods can be mocked using Powermock.

import static org.mockito.Mockito.*;
import static org.powermock.api.mockito.PowerMockito.*;
import static org.powermock.core.classloader.annotations.PrepareForTest.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(MathUtil.class)
public class MathUtilTest {
    @Test
    public void testStaticMethod() {
        mockStatic(MathUtil.class);
        when(MathUtil.add(10, 20)).thenReturn(30);

        int result = MathUtil.add(10, 20);
        assertEquals(30, result);
    }
}

class MathUtil {
    public static int add(int a, int b) {
        return a + b;
    }
}

Explanation: This example shows how to mock the static method MathUtil.add(). The mockStatic() method is used to mock the static method, and when() is used to define the behavior of the mocked method.

Output:

No output if assertions pass.

Mocking Constructors

Powermock can also mock constructors to return mock objects.

import static org.mockito.Mockito.*;
import static org.powermock.api.mockito.PowerMockito.*;
import static org.powermock.core.classloader.annotations.PrepareForTest.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(Student.class)
public class StudentTest {
    @Test
    public void testMockConstructor() throws Exception {
        Student mockStudent = mock(Student.class);
        whenNew(Student.class).withAnyArguments().thenReturn(mockStudent);
        when(mockStudent.getName()).thenReturn("Amit");

        Student student = new Student("Amit");
        assertEquals("Amit", student.getName());
    }
}

class Student {
    private String name;

    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

Explanation: This example shows how to mock the constructor of the Student class using whenNew(). The constructor is mocked to return a mock object, and the behavior of the mock object is defined using when().

Output:

No output if assertions pass.

Mocking Private Methods

Powermock can mock private methods using the Whitebox utility.

import static org.mockito.Mockito.*;
import static org.powermock.api.mockito.PowerMockito.*;
import static org.powermock.core.classloader.annotations.PrepareForTest.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;

@RunWith(PowerMockRunner.class)
@PrepareForTest(Employee.class)
public class EmployeeTest {
    @Test
    public void testMockPrivateMethod() throws Exception {
        Employee employee = spy(new Employee("Rajesh"));
        doReturn("Hello, Rajesh").when(employee, "greet");

        String greeting = Whitebox.invokeMethod(employee, "greet");
        assertEquals("Hello, Rajesh", greeting);
    }
}

class Employee {
    private String name;

    public Employee(String name) {
        this.name = name;
    }

    private String greet() {
        return "Hello, " + name;
    }
}

Explanation: This example shows how to mock a private method using Whitebox.invokeMethod(). The private method greet() is mocked to return a custom value.

Output:

No output if assertions pass.

Advanced Features

Mocking Final Methods and Classes

Powermock can mock final methods and classes.

import static org.mockito.Mockito.*;
import static org.powermock.api.mockito.PowerMockito.*;
import static org.powermock.core.classloader.annotations.PrepareForTest.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(FinalClass.class)
public class FinalClassTest {
    @Test
    public void testMockFinalMethod() {
        FinalClass finalClass = mock(FinalClass.class);
        when(finalClass.finalMethod()).thenReturn("Mocked Result");

        String result = finalClass.finalMethod();
        assertEquals("Mocked Result", result);
    }
}

final class FinalClass {
    public final String finalMethod() {
        return "Original Result";
    }
}

Explanation: This example shows how to mock a final method in a final class using Powermock. The final method finalMethod() is mocked to return a custom value.

Output:

No output if assertions pass.

Mocking Static Initializers

Powermock can mock static initializers using the @PrepareForTest annotation.

import static org.mockito.Mockito.*;
import static org.powermock.api.mockito.PowerMockito.*;
import static org.powermock.core.classloader.annotations.PrepareForTest.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(StaticInitializer.class)
public class StaticInitializerTest {
    @Test
    public void testMockStaticInitializer() {
        mockStatic(StaticInitializer.class);
        when(StaticInitializer.getValue()).thenReturn("Mocked Value");

        String value = StaticInitializer.getValue();
        assertEquals("Mocked Value", value);
    }
}

class StaticInitializer {
    private static String value;

    static {
        value = "Original Value";
    }

    public static String getValue() {
        return value;
    }
}

Explanation: This example shows how to mock a static initializer using Powermock. The static initializer in the StaticInitializer class is mocked to return a custom value.

Output:

No output if assertions pass.

Verifying Static Method Calls

Powermock allows verification of static method calls.

import static org.mockito.Mockito.*;
import static org.powermock.api.mockito.PowerMockito.*;
import static org.powermock.core.classloader.annotations.PrepareForTest.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(VerificationUtil.class)
public class VerificationUtilTest {
    @Test
    public void testVerifyStaticMethod() {
        mockStatic(VerificationUtil.class);
        VerificationUtil.log("Logging message");

        verifyStatic(VerificationUtil.class);
        VerificationUtil.log("Logging message");
    }
}

class VerificationUtil {
    public static void log(String message) {
        System.out.println(message);
    }
}

Explanation: This example shows how to verify that a static method was called using verifyStatic(). The static method log() is verified to have been called with a specific argument.

Output:

No output if assertions pass.

Mocking New Instances Created Inside Methods

Powermock can mock new instances created inside methods.

import static org.mockito.Mockito.*;
import static org.powermock.api.mockito.PowerMockito.*;
import static org.powermock.core.classloader.annotations.PrepareForTest.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(Service.class)
public class ServiceTest {
    @Test
    public void testMockNewInstance() throws Exception {
        Service mockService = mock(Service.class);
        whenNew(Service.class).withNoArguments().thenReturn(mockService);
        when(mockService.process()).thenReturn("Mocked Process");

        Client client = new Client();
        String result = client.processRequest();
        assertEquals("Mocked Process", result);
    }
}

class Client {
    public String processRequest() {
        Service service = new Service();
        return service.process();
    }
}

class Service {
    public String process() {
        return "Original Process";
    }
}

Explanation: This example shows how to mock new instances created inside a method using whenNew(). The Service class instance created inside the processRequest() method of the Client class is mocked.

Output:

No output if assertions pass.

Conclusion

Powermock is a powerful and flexible library for extending the capabilities of mock frameworks like Mockito. This guide covered the basics of using Powermock, including mocking static methods, constructors, private methods, final methods, and static initializers. Additionally, it demonstrated advanced features such as verifying static method calls and mocking new instances created inside methods. By leveraging Powermock, you can handle complex mocking scenarios and improve your test coverage. For more detailed information and advanced features, refer to the official Powermock documentation.

Comments