logo
Solutionlegacy

Mocking final methods and classes with Powermock

Views: 825 Created: 2019-06-12 Read time: 4 minutes
Post Preview
Tags:

1)     Preface

Legacy code may seem overwhelming at times. In most cases, to be exact. A lot of times, there is even no one left from the original team that created it. No help, long gone or heavily outdated documentation, multi-thousand line monster classes fruitful in multi-hundred line methods that contain dozens upon dozens of logical paths. We have all been there and seen it. Due to these traits, the legacy code is tough to test. Sometimes impossible without special tools. PowerMock lends us a helping hand in this kind of situations where the methods under test are not crazy big but contain references to, hard to test language constructs like static methods, private methods and the subject of this post: the final methods and classes. Let us take a look at how we can handle them with the use of this powerful tool.

 

Note
Note

 

2)     Powermock: mocking final method

We will start with an example where in our SUT, we are forced to call a final method. We do not want to invoke the implementation of that method as it is very complex and not really the point of interest of our test case. Let's look at the code example where we are querying the database of Auctions to find the most popular ones for a given categoryId and then using some additional logic to filter out the final result:

 

SUT Code


private static final Integer MOST_POPULAR_CAP = 10;

@Autowired private AuctionRepository auctionRepository;
@Autowired private PopularityResolver popularityResolver;

public List<Auction> getMostPopularAuctionsFromCategory(Integer categoryId) {
	List<Auction> initialPopularAuctionCandidates = auctionRepository
			.findByCategoryOrderByViewsDesc(categoryId, Integer.valueOf(20));

	List<Auction> topAuctions = popularityResolver
			.resolveWithCap(initialPopularAuctionCandidates, MOST_POPULAR_CAP);

	return topAuctions;
}

In this SUT method, we are:

       (L8):  Retrieving twenty most viewed active Auctions.

       (L11):  Invoking the resolveWithCap method of PopularityResolver. This is a third-party written library, and we cannot make any changes to it, unfortunately. On top of that, it is a final method and queries resources that we do not want to invoke during our UT.

Despite these obstacles, we still need to pull off the test somehow. PowerMock enables us to stub the final method and omit the real implementation being invoked:

 

Test Code


@RunWith(PowerMockRunner.class)
@PrepareForTest( { PopularityResolver.class })
public class BasicAuctionServiceTest {

	@Mock private AuctionRepository auctionRepositoryStub;
	@InjectMocks private BasicAuctionService basicAuctionServiceSUT;

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

	@Test
	public void shouldGetMostPopularAuctionsFromCategory() throws Exception{
		// Arrange
		Integer categoryId = Integer.valueOf(10);
		List<Auction> initialAuctions = new ArrayList<>();
		List<Auction> expectedAuctions = new ArrayList<>();

		AuctionRepository auctionRepositoryStub = Mockito.mock(AuctionRepository.class);
		Mockito.when(auctionRepositoryStub.findByCategoryOrderByViewsDesc(categoryId, Integer.valueOf(20)))
				.thenReturn(initialAuctions);

		PopularityResolver popularityResolverStub = PowerMockito.mock(PopularityResolver.class);
		basicAuctionServiceSUT.setPopularityResolver(popularityResolverStub);
		PowerMockito.when(popularityResolverStub.resolveWithCap(initialAuctions, Integer.valueOf(10)))
				.thenReturn(expectedAuctions);

		// Act
		List<Auction> mostPopularAuctionsFromCategory = basicAuctionServiceSUT
				.getMostPopularAuctionsFromCategory(categoryId);

		// Assert
		assertThat(mostPopularAuctionsFromCategory).isSameAs(expectedAuctions);
	}

In this test method, we are:

       (L3):  Preparing the PopularityResolver class for PowerMock to be able to stub its resolveWithCap method.

       (L25):  Mocking the PopularityResolver class.

       (L27):  Stubbing the final resolveWithCap method.

       (L35):  Making sure that the returned list is as we expect it to be.

 

Tip
Tip

 

3)     Powermock: mocking final method with wildcards

In this example, we will try to go through an alternative version of the previous code. Let us take a look:

 

SUT Code


@Autowired private AuctionRepository auctionRepository;
@Autowired private PopularityResolver popularityResolver;
@Autowired private Config config;

public List<Auction> getMostPopularAuctionsFromCategory(Integer categoryId) {
	List<Auction> initialPopularAuctionCandidates = auctionRepository
			.findByCategoryOrderByViewsDesc(categoryId, Integer.valueOf(20));

	List<Auction> topAuctions = popularityResolver
			.resolveWithCap(initialPopularAuctionCandidates,
							config.getInt("MOST_POPULAR_CAP"));

	return topAuctions;
}

As we can see how the MOST_POPULAR_CAP value is taken from an external properties file. We do not want to read a file, and in that case, we do not really care about expecting any particular value here. If it passes an Integer type check, it is sufficient:

 

Test Code


@RunWith(PowerMockRunner.class)
@PrepareForTest( { PopularityResolver.class })
public class BasicAuctionServiceTest {

	@Mock private AuctionRepository auctionRepositoryStub;
	@Mock private Config configStub;
	@InjectMocks private BasicAuctionService basicAuctionServiceSUT;

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

	@Test
	public void shouldGetMostPopularAuctionsFromCategory() throws Exception{
		// Arrange
		Integer categoryId = Integer.valueOf(10);
		List<Auction> initialAuctions = new ArrayList<>();
		List<Auction> expectedAuctions = new ArrayList<>();

		AuctionRepository auctionRepositoryStub = Mockito.mock(AuctionRepository.class);
		Mockito.when(auctionRepositoryStub.findByCategoryOrderByViewsDesc(categoryId, Integer.valueOf(20)))
				.thenReturn(initialAuctions);

		PopularityResolver popularityResolverStub = PowerMockito.mock(PopularityResolver.class);
		basicAuctionServiceSUT.setPopularityResolver(popularityResolverStub);

		PowerMockito.when(popularityResolverStub.resolveWithCap(
			ArgumentMatchers.eq(initialAuctions), ArgumentMatchers.anyInt()))
				.thenReturn(expectedAuctions);

		// Act
		List<Auction> mostPopularAuctionsFromCategory = basicAuctionServiceSUT
				.getMostPopularAuctionsFromCategory(categoryId);

		// Assert
		assertThat(mostPopularAuctionsFromCategory).isSameAs(expectedAuctions);
	}

In this test method, we are:

       (L7):  Creating a stub from the Config class that normally would read properties file.

       (L29-31):  Stubbing the resolveWithCap method so that it returns the expectedAcutions list when an object equal to initialAuctions is passed along with any Integer.

Take note that we do not need to explicitly stub here the config.getInt method. It would return by default a NULL value which is still fine for the ArgumentMatchers.anyInt method to pass.

 

Note
Note

 

4)     Powermock: mocking final class

Sometimes it is not the method that causes the problem but the class itself. It is a common practice and 100% justified to mark utility classes as final. Usually the methods in such classes are marked as static, but in our fabulous legacy app someone insisted on creating the class object every time there is a need to invoke one of its public methods:

 

SUT Code


@Autowired private AuctionRepository auctionRepository;
@Autowired private PopularityResolver popularityResolver;
private CategoryUtilities categoryUtilities; // setter provided

public List<Auction> getMostPopularAuctionsFromCategory(Integer categoryId) {
	List<Auction> initialPopularAuctionCandidates = auctionRepository
			.findByCategoryOrderByViewsDesc(categoryId, Integer.valueOf(20));

	Integer mostPopularCap = categoryUtilities.getCapForCategory(categoryId);

	List<Auction> topAuctions = popularityResolver
			.resolveWithCap(initialPopularAuctionCandidates, mostPopularCap);

	return topAuctions;
}

Here we get the cap from CategoryUtilities.getCapForCategory method. Again we do not want to call this method as it uses some external resources which are out of the scope of this test case. The CategoryUtilities class is final though so we need to ask PowerMock for some extra assistance:

 

Test Code


@RunWith(PowerMockRunner.class)
@PrepareForTest( { PopularityResolver.class, CategoryUtilities.class })
public class BasicAuctionServiceTest {

	@Mock private AuctionRepository auctionRepositoryStub;
	@InjectMocks private BasicAuctionService basicAuctionServiceSUT;

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

	@Test
	public void shouldGetMostPopularAuctionsFromCategory() throws Exception{
		// Arrange
		Integer categoryId = Integer.valueOf(10);
		List<Auction> initialAuctions = new ArrayList<>();
		List<Auction> expectedAuctions = new ArrayList<>();

		AuctionRepository auctionRepositoryStub = Mockito.mock(AuctionRepository.class);
		Mockito.when(auctionRepositoryStub.findByCategoryOrderByViewsDesc(categoryId, Integer.valueOf(20)))
				.thenReturn(initialAuctions);

		PopularityResolver popularityResolverStub = PowerMockito.mock(PopularityResolver.class);
		CategoryUtilities categoryUtilitiesStub = PowerMockito.mock(CategoryUtilities.class);

		basicAuctionServiceSUT.setPopularityResolver(popularityResolverStub);
		basicAuctionServiceSUT.setCategoryUtilities(categoryUtilitiesStub);

		PowerMockito.when(popularityResolverStub.resolveWithCap(
			ArgumentMatchers.eq(initialAuctions), ArgumentMatchers.anyInt()))
				.thenReturn(expectedAuctions);

		// Act
		List<Auction> mostPopularAuctionsFromCategory = basicAuctionServiceSUT
				.getMostPopularAuctionsFromCategory(categoryId);

		// Assert
		assertThat(mostPopularAuctionsFromCategory).isSameAs(expectedAuctions);
	}

Here we have to add our CategoryUtilities class to the @PrepareForTest annotation along with the existing PopularityResolver. Also, on (L26), we mock the final class. Again we do not care what the exact Integer value is passed into the resolveWithCap method, so the Mockito default return value is sufficient.

 

Tip
Tip

 

5)     Conclusion

 

Summary

 

We have seen PowerMock in action when it comes to dealing with final methods and classes. We must remember that in the non-legacy code, there is normally a better way to design your solution without the need to use finals directly. Anyway if there is no other option, PowerMock helps us in achieving that final lift!

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