logo
Solutionlegacy

Mocking static methods with PowerMock

Views: 263 Created: 2019-01-26 Read time: 4 minutes
Post Preview
Tags:

1)     Preface

One of the features, if you can call it that way, of legacy code, is the fact that it is riddled with statics. Technical debt has been growing steadily. That is why developers started to reach for this language construct to omit the problematic creation of particular objects. They want to bypass that by assembling some statics that do not require instantiation. This makes the code less and less object-oriented and more functional you might say. Having that in mind, it is also a lot harder to test. Some of the static methods are not trivial, and in the context of a unit test should be simply mocked/omitted. If there is no hope of refactoring the code, then PowerMock allows us to bypass some of the static implementations and focus on the implementation of the SUT exclusively.

 

Note
Note

 

2)     Powermock: mocking no-arg static method

Let us start with a basic static argument-less call. In this Restaurant Management application, we have a method where we are trying to book a Table for a Customer:

 

SUT Code


@Autowired private RestaurantSystem restaurantSystem;
@Autowired private Manager manager;

public void bookTable(Customer customer){
	manager.answerCall(customer);

	Table table = Manager.findFreeTable();

	if(table == null){
		restaurantSystem.notifyCustomerWhenTablesAvailable(customer);
		manager.appologiseAndConfirmNotification(customer);

		return;
	}

	restaurantSystem.bookTable(table);
	manager.confirmBooking(customer);
}

public class Manager{

	public static Table findFreeTable(){
		// operation involving many collaborators
	}

In this SUT method, we are:

       (L6):  Noting down the customer name and time he wants to book a table with us.

       (L8):  Calling the static Manager method to find an actual Table. This method calls a bunch of collaborators and is not trivial.

       (L17):  Booking the table and confirming with the Customer.

Now we would like to test the happy path scenario where we actually find a free Table and book it for our Customer. We have to do something with the static method for it to return a Table for us:

 

Test Code


@RunWith(PowerMockRunner.class)
@PrepareForTest(Manager.class)
public class RestaurantControllerTest {

    @InjectMocks private RestaurantController restaurantControllerSUT;
    @Mock private RestaurantSystem restaurantSystemMock;
    @Mock private Manager managerStub;

    @Before
    public void init(){
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void shouldBookATableForCustomer() throws Exception{
        // Arrange
        Table table = new Table();
        Customer customer = new Customer();
        PowerMockito.mockStatic(Manager.class);
        PowerMockito.when(Manager.findFreeTable()).thenReturn(table);

        // Act
        restaurantControllerSUT.bookTable(customer);

        // Assert
        Mockito.verify(restaurantSystemMock).bookTable(table);
    }

In this test method, we are:

       (L2,3):  Preparing our test class for PowerMock to be able to stub certain static methods.

       (L6-8):  Using standard Mockito to initialise collaborators as stubs.

       (L20):  Stubbing the static Manager.findFreeTable method for it to return a concrete Table object.

       (L27):  Making sure that the proper table has been booked.

 

Tip
Tip

 

3)     Powermock: mocking static method with arguments

Most of the time, our static methods will accept a bunch of arguments. PoweMock is ready for these scenarios. Let's look at an altered version of the previous code where the outcome of the findFreeTable will be dependent on some input params:

 

SUT Code


public static final int LEAVE_AT_LEAST_NUMBER_OF_SEATS = 10;
public static final int LATEST_HOUR = 20;

public void bookTable(Customer customer){
	manager.answerCall(customer);

	Table table = manager.findFreeTable(LEAVE_AT_LEAST_NUMBER_OF_SEATS, LATEST_HOUR);

	if(table == null){
		restaurantSystem.notifyCustomerWhenTablesAvailable(customer);
		manager.appologiseAndConfirmNotification(customer);

		return;
	}

	restaurantSystem.bookTable(table);
	manager.confirmBooking(customer);
}

Now in our test, we simply expect the stubbed static method to be called with very specific attributes.

 

Test Code


@Test
public void shouldBookATableForCustomer() throws Exception{
	// Arrange
	Table table = new Table();
	Customer customer = new Customer();
	PowerMockito.mockStatic(Manager.class);
	PowerMockito.when(Manager.findFreeTable(10,20)).thenReturn(table);

	// Act
	restaurantControllerSUT.bookTable(customer);

	// Assert
	Mockito.verify(restaurantSystemMock).bookTable(table);
}

 

Note
Note

 

4)     Powermock: mocking static method with wildcards

There are situations where we do not really care about the exact values that are passed to a stubbed method. We really just want to make sure that they are of the proper type. Let's look at the third incarnation of our bookATable method where the arguments passed into the findFreeTable static method are taken from a properties file:

 

SUT Code


public static final String LEAVE_AT_LEAST_NUMBER_OF_SEATS_PARAM = "least.seats";
public static final String LATEST_HOUR_PARAM = "latest.hour";

@Autowired private Configuration configuration;

public void bookTable(Customer customer){
	manager.answerCall(customer);

	Table table = manager.findFreeTable(configuration.getInt(LEAVE_AT_LEAST_NUMBER_OF_SEATS_PARAM),
			configuration.getInt(LATEST_HOUR_PARAM));

	if(table == null){
		restaurantSystem.notifyCustomerWhenTablesAvailable(customer);
		manager.appologiseAndConfirmNotification(customer);

		return;
	}

	restaurantSystem.bookTable(table);
	manager.confirmBooking(customer);
}

These values will vary based on the environment the application is running on, so we do not care about being precise here:

 

Test Code


@Mock private Configuration configuration;

@Test
public void shouldBookATableForCustomer3() throws Exception{
	// Arrange
	Table table = new Table();
	Customer customer = new Customer();
	PowerMockito.mockStatic(Manager.class);
	PowerMockito.when(Manager.findFreeTable(ArgumentMatchers.anyInt(),ArgumentMatchers.anyInt()))
			.thenReturn(table);

	// Act
	restaurantControllerSUT.bookTable(customer);

	// Assert
	Mockito.verify(restaurantSystemMock).bookTable(table);
}

Mockito's ArgumentMatchers allows us to implement wildcards in our stubbing easily.

 

Tip
Tip

 

5)     Powermock: spying on a static method

Sometimes we do not want to stub all of the static methods of a particular class. We would like to call real implementations occasionally as the code is quite simple and does not require any additional configuration in our test. Let's in that case go through an example where a Waiter will try receive a payment for Bill from our Customer:

 

SUT Code


public Bill processBill(Customer customer, Bill bill){
	Iterator<CreditCard> creditCards = customer.prepareToPay();

	while(!bill.paid()){
		if(creditCards.hasNext()){
			CreditCard creditCard = creditCards.next();

			boolean transactionSuccessful = Waiter.performTransaction(bill, creditCard);

			if(transactionSuccessful){
				bill.setPaid(true);
				break;
			}else{
				Waiter.informTransactionNotAccepted(customer, bill);
			}
		}else{
			throw new RuntimeException("Customer cannot pay");
		}
	}

	return bill;
}

In this SUT method, we are:

       (L9):  Trying to perform a transaction on a CreditCard that the Customer has provided to us.

       (L12):  Marking the Bill as paid.

       (L15):  Informing the Customer that the transaction has been declined. This is a straightforward method. We will not need to stub it in any way.

Let's try to write a test for a scenario when the first CreditCard is declined, but the next one passes:

 

Test Code


@Test
public void shouldProcessBill_afterUnsuccessfullAttempt() throws Exception{
	// Arrange
	Customer customer = new Customer();
	customer.setCreditCards(Arrays.asList(new CreditCard[]{new CreditCard(1), new CreditCard(2)}));

	PowerMockito.spy(Waiter.class);
	PowerMockito.when(Waiter.performTransaction(
			ArgumentMatchers.any(Bill.class), ArgumentMatchers.any(CreditCard.class)))
		.thenReturn(false)
		.thenReturn(true);

	// Act
	Bill processedBill = restaurantControllerSUT.processBill(customer, new Bill());

	// Assert
	assertThat(processedBill.paid()).isTrue();
}

In this test method, we are:

       (L8):  Spying on the Waiter static methods. This means that all of the static methods real implementation will get invoked by default.

       (L9):  Stubbing the static Waiter.performTransaction method so that it returns a false for the first time followed by a true value.

       (L18):  Expecting for the returned Bill to be paid off.

 

6)     Conclusion

 

Summary

 

We have seen how easy static method stubbing is when we use PowerMock. The stubbing options are literally the same as for vanilla Mockito feature. That means if you worked with Mockito before you will feel like a fish in the water. So do not get stuck in your legacy code waiting for it to eat you up. Get hold of PowerMock and get those bulky statics stubbed!

Need more insight?
Repository
Repository
Glossary
Glossary
Tags:
Reference
You may also like:
mocking-private-methods-with-powermock
Mocking private methods with PowerMock
mocking-final-methods-and-classes-with-powermock
Mocking final methods and classes with Powermock
mocking-object-creation-with-powermock
Mocking object creation with PowerMock
Comments
Be the first to comment.
Leave a comment