logo
Solutionlegacy

Mocking private methods with PowerMock

Views: 5765 Created: 2019-06-28 Read time: 5 minutes
Post Preview
Tags:

1)     Preface

Read it, close it, do what you need to do, do not tell anyone about it. If there is one single thing that everyone tells you not to do in a unit test is just this: do not test private methods! They are an implementation detail which can change more often than you think. Well, that is all true, but if there were really no scenarios for private method testing, then you would not be reading this post. So where is the catch? Legacy code.. yes that big pile of bricks sitting around (and haunting) somewhere on an archived CVS repository (anyone remembers this thing?!).

 

And you got the privilege to dig it out and implement a few changes. Come one do not tell me you never dreamt about it. Ok, your team lead also underlines that based on the newest policy, all the changes need to have some test automation applied to it (now that is a good policy). My refactoring skill is not top notch yet, so how am going to test all those public methods riddled with humongous private methods?

 

Let us take a look at how PowerMock allows us to stub private methods of our SUT without the need of using reflection.

 

Note
Note

 

2)     Powermock: mocking no-arg private void method

When it comes to private methods, we will need to mock the direct methods of the SUT. This means that we would need to use spies as we need to have all of its public methods that we need to test, invoke their real implementation. Let's first take a look at a method which tries to place a Bid on a given Auction:

 

SUT Code


private static final Integer MAX_BID_ATTEMPTS = Integer.valueOf(3);

@Autowired private BidSaver bidSaver;
@Autowired private LoggingService loggingService;

public boolean bidOnAuction(Integer auctionId, Bid bid){
	Integer attemptCount = Integer.valueOf(0);
	boolean bidSuccessfull = false;

	while(attemptCount < MAX_BID_ATTEMPTS){
		try{
			bidSuccessfull = bidSaver.saveBidOnAuction(auctionId, bid);
			break;
		}catch(AuctionLockedException e){
			loggingService.logBidUnsuccessfull(auctionId, bid);
			attemptCount++;
		}

		waitABit();
	}

	return bidSuccessfull;
}

private void waitABit(){
	try {
		Thread.sleep(3000);
	} catch (InterruptedException e) {}
}

In this SUT method, we:

       (L13):  We try to save the Bid for an auctionId.

       (L15):  In another user is trying to bid at the same time we get an AuctionLockedException.

       (L20):  We log the error, and waitABit() a moment to retry. We have 3 attempts in total. Otherwise, the whole action is unsuccessful.

Now the private waitABit method may take up to 3 seconds to finish, and for our test suite, it is unacceptable. Let's try to test a scenario when an auction is successfully saved after a failed try and without the need to wait 3 seconds for the next attempt:

 

Test Code


@RunWith(PowerMockRunner.class)
@PrepareForTest(BasicAuctionService.class)
public class BasicAuctionServiceTest {

	@Mock private BidSaver bidSaverStub;
	@Mock private LoggingService loggingServiceStub;
	@Mock private Bid dummyBid;

	@InjectMocks
	@Spy
	private BasicAuctionService basicAuctionServiceSUT = new BasicAuctionService();

	@Test
	public void shouldRetryBidOnAuction() throws Exception{
		// Arrange
		Integer auctionId = Integer.valueOf(10);

		when(bidSaverStub.saveBidOnAuction(auctionId,dummyBid))
				.thenThrow(new AuctionLockedException())
				.thenReturn(true);

		PowerMockito.doNothing().when(basicAuctionServiceSUT, "waitABit");

		// Act
		boolean success = basicAuctionServiceSUT.bidOnAuction(auctionId, dummyBid);

		// Assert
		assertThat(success).isTrue();
		verify(bidSaverStub, times(2)).saveBidOnAuction(auctionId, dummyBid);
	}

In this test method, we are:

       (L3):  Preparing our SUT class for PowerMock to be able to mock our private method.

       (L12):  Spying on our SUT.

       (L23):  Stubbing the waitABit method so that it does nothing when it is invoked.

       (L29-30):  Making sure that save was a success and the saveBidOnAuction has been called twice.

 

Note
Note

 

3)     Powermock: mocking private method with arguments

Most of the time, we will be dealing with methods that accept arguments. Let's try to take a look at the SUT method from the previous example, but this time, the waitABit method will accept the number of milliseconds:

 

SUT Code


public static final Integer WAIT_MILIS = Integer.valueOf(3000);

public boolean bidOnAuction(Integer auctionId, Bid bid){
	Integer attemptCount = Integer.valueOf(0);
	boolean bidSuccessfull = false;

	while(attemptCount < MAX_BID_ATTEMPTS){
		try{
			bidSuccessfull = bidSaver.saveBidOnAuction(auctionId, bid);
			break;
		}catch(AuctionLockedException e){
			loggingService.logBidUnsuccessfull(auctionId, bid);
			attemptCount++;
		}

		waitABit(WAIT_MILIS);
	}

	return bidSuccessfull;
}

private void waitABit(Integer milis){
	try {
		Thread.sleep(milis);
	} catch (InterruptedException e) {}
}

Let's see a test where we except the save to fail due to the max number of attempts being exceeded. Again, we would like to omit the implementation of the waitABit method:

 

Test Code


@RunWith(PowerMockRunner.class)
@PrepareForTest(BasicAuctionService.class)
public class BasicAuctionServiceTest {

	@Mock private BidSaver bidSaverStub;
	@Mock private LoggingService loggingServiceStub;
	@Mock private Bid dummyBid;

	@InjectMocks
	@Spy
	private BasicAuctionService basicAuctionServiceSUT = new BasicAuctionService();

	@Test
    public void shouldFailBidOnAuction_whenToManyAttempts() throws Exception{
        // Arrange
        Integer auctionId = Integer.valueOf(10);

        when(bidSaverStub.saveBidOnAuction(auctionId,dummyBid))
                .thenThrow(new AuctionLockedException());

        PowerMockito.doNothing().when(
			basicAuctionServiceSUT, "waitABit", Integer.valueOf(3000));

        // Act
        boolean success = basicAuctionServiceSUT.bidOnAuction(auctionId, dummyBid);

        // Assert
        assertThat(success).isFalse();
        verify(bidSaverStub, times(3)).saveBidOnAuction(auctionId, dummyBid);
    }

In this test method, we are:

       (L19):  Making sure that every attempt ends up in an exception being thrown.

       (L22):  Stubbing the waitABit method so that, when an integer of 3000 is passed, we do not invoke the real implementation.

       (L28):  Verifying that the operation did not succeed.

 

Tip
Tip

 

4)     Powermock: mocking private method with wildcards

Finally, sometimes we do not really care about the actual value of the input as long as it is of a certain type. For example, an injected value from a properties file that varies based on the environment on which we run the application:

 

SUT Code


@Value("#{config.save.timeout}")
private int waitMilis;

public boolean bidOnAuction(Integer auctionId, Bid bid){
	Integer attemptCount = Integer.valueOf(0);
	boolean bidSuccessfull = false;

	while(attemptCount < MAX_BID_ATTEMPTS){
		try{
			bidSuccessfull = bidSaver.saveBidOnAuction(auctionId, bid);
			break;
		}catch(AuctionLockedException e){
			loggingService.logBidUnsuccessfull(auctionId, bid);
			attemptCount++;
		}

		waitABit(waitMilis);
	}

	return bidSuccessfull;
}

private void waitABit(Integer milis){
	try {
		Thread.sleep(milis);
	} catch (InterruptedException e) {}
}

In this SUT method, we are:

       (L2):  Injecting the timeout from a properties file.

       (L18):  Using the injected value in our waitABit method.

 

Now let's rewrite the previous test, but we would not care about the actual value passed to the waitABit method:

 

Test Code


@RunWith(PowerMockRunner.class)
@PrepareForTest(BasicAuctionService.class)
public class BasicAuctionServiceTest {

	@Mock private BidSaver bidSaverStub;
	@Mock private LoggingService loggingServiceStub;
	@Mock private Bid dummyBid;

	@InjectMocks
	@Spy
	private BasicAuctionService basicAuctionServiceSUT = new BasicAuctionService();

	@Test
    public void shouldFailBidOnAuction_whenToManyAttempts() throws Exception{
        // Arrange
        Integer auctionId = Integer.valueOf(10);

        when(bidSaverStub.saveBidOnAuction(auctionId,dummyBid))
                .thenThrow(new AuctionLockedException());

        PowerMockito.doNothing().when(
			basicAuctionServiceSUT, "waitABit", ArgumentMatcher.anyInt());

        // Act
        boolean success = basicAuctionServiceSUT.bidOnAuction(auctionId, dummyBid);

        // Assert
        assertThat(success).isFalse();
        verify(bidSaverStub, times(3)).saveBidOnAuction(auctionId, dummyBid);
    }

Here we use the Mockito's ArgumentMatchers.anyInt to achieve a wildcard outcome. All the primitive types have their own version of the wildcard along with anyString() and anyObject().

 

5)     Conclusion

Summary

 

 

Thanks to PowerMock, we can omit those nasty private methods in our legacy code. We need to keep in mind though that this technique should be done only if no refactoring is possible for some reason. Even then, we should be doing it when no one can really see us and try to keep it as our private little secret!

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