Mocking Static Methods, Protected/Private Methods, & Spring Controllers

We recently had to write unit tests on a project with 0% code coverage. Here, I’ll talk about some of the mocking and other challenges we faced.

If you’ve ever been involved in a major effort to go from 0% code coverage to the coveted 80%, you know that it’s no easy task. The reason is due not only to the sheer number of staff hours but also to the fact that, if your code is anything like mine, your code may reflect bad practices. (For the record, I believe that you should aim for as much coverage as is necessary to comfortably release your code -- not necessarily 80%.)

“I expect a high level of coverage. Sometimes managers require [a high level of coverage]. There's a subtle difference.” -Brian Marick

If you ever find yourself desperately trying to increase your code coverage, I would recommend devoting just a small, agreed upon percentage of sprint points to cleaning up technical debt (essentially, any debt you owe to your code for taking shortcuts along the way). That means not unit testing, not documenting, not fixing code smells, and a slew of other things. In order for this to work, everyone would have to follow the old boy scout rule, “I will not increase the technical debt.” Slowly but surely, you will see a difference. (Be on the lookout for a future blog post about managing technical debt.)

We recently had to start writing unit tests on a project with 0% code coverage. Below, I’ll talk about some of the challenges we faced -- challenges many others are likely to encounter themselves.

Before doing a deeper dive into mocking, let’s start with some terminology you’re likely to come across.

Mocks vs. Stubs

It took me some time to grasp the difference between mocks and stubs. This helpful stackoverflow page provides a starting point for understanding the difference. Also, here are Martin Fowler’s definitions of some important terms:

  • Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists.
  • Fake objects actually have working implementations but usually take some shortcut, which makes them unsuitable for production (an in memory database is a good example).
  • Stubs provide canned answers to calls made during the test and usually don’t respond at all to anything outside what's programmed in for the test.
  • Spies are stubs that also record some information based on how they were called. One form might be an email service that records how many messages were sent.
  • Mocks are objects pre-programmed with expectations that form a specification of the calls they are expected to receive.

While Fowler’s definitions are useful, specifically for more advanced developers, there’s still a lot of debate around these terms. Let me make this a bit simpler. All of these terms, at the end of the day, are just mocks. In other words, they are just fake classes. Whether or not we choose to interact with the mock to check for behavior doesn’t make it any less of a mock.

I’ll give you an example for the two terms that gave me the most trouble, mocks and stubs.

An example of a stub would be having a class that sends an alert. Instead of sending the alert, we can stub that functionality out and return some other dummy values.

With a mock, on the other hand, we care about behavior. For example, when we call the bakeCake() method, we are confirming that we are in turn calling the mixIngredients() and preheatOven() methods.

Mocking Protected Methods

Let’s jump right into mocking. Here are a couple of Mockito scenarios we had to work around.

Oftentimes you will find yourself dealing with protected and private methods. How can you override the return of a protected method from your test? When testing a method with an underlying call to a protected method that you’d like to mock, one easy solution is to simply create a class extending the mocked class.

The class to test:

public class UnderTest extends DependentClass {

public String regularMethod() {
return protectedMethod() + " and I am using it from UnderTest";
}
}

The extended class:

public class DependentClass {

protected String protectedMethod(){
return "This method is protected";
}
}

And the test:

public class UnderTestTest {

class MockClass extends UnderTest{

@Override //This method will now serve as an override to that protected one from DependentClass
public String protectedMethod(){
return "This method used to be protected";
}
}

@Test
public void regularMethodTest(){
MockClass mc = mock(MockClass.class); //The mock method creates our mock class for us
//When encountering the protected method, call the real method (my override one)
when(mc.protectedMethod()).thenCallRealMethod();
when(mc.regularMethod()).thenCallRealMethod();
assertEquals(mc.regularMethod(), "This method used to be protected and I am using it from UnderTest");
}

}

You can see from the code above that now we are actually creating a new class called MockClass that extends UnderTest. We have access to override any underlying method within that class (and any subsequent classes). Using the When/Then notation, I can force the call to one of my methods to call the real override method that was created. You can also override this value too, as in this example:

@Test
public void regularMethodTest(){
MockClass mc = mock(MockClass.class);
//When encountering the protected method, call the real method (my override one)
when(mc.protectedMethod()).thenReturn("Double override");
when(mc.regularMethod()).thenCallRealMethod();
assertEquals(mc.regularMethod(), "Double override and I am using it from UnderTest");
}

Here is a basic model of what we are doing:

Mocking

Web MVC Testing

Now we need a way to test a regular spring controller. We have a basic app that has a rest controller (PostController) for posting links to a page.

PostController:

@RestController
public class PostController {

private PostRepository repository;
public PostController(PostRepository repository) {
this.repository= repository;
}

@GetMapping("/posts")
public Collection<Post> posts() {
return repository.findAll().stream().collect(Collectors.toList());
}

@PostMapping("/post")
public Post post(Post post) {
return repository.save(post);
}
@DeleteMapping("/post")
public void deletePost(Post post) {
repository.delete(post);
}
}

The PostController makes reference to a basic @Repository and a post class containing fields for a name, title, link, etc.

Here’s how we test it:

@WebMvcTest(PostController.class) //This annotation is provided by Spring and allows us to only focus on Spring MVC components by applying only MVC test configurations
public class PostControllerTest {
@Autowired
private MockMvc mockMvc; //our gateway to executing the calls

@InjectMocks
PostController mockPost;

@Mock
PostRepository mockPr;

@Before
public void setup() {
initMocks(this);
this.mockMvc = standaloneSetup(mockPost).build();
}

@Test
getPostsResponse
public void () throws Exception {
this.mockMvc.perform(get("/posts"))
.andDo(print())
.andExpect(status().isOk());
}

@Test
public void postPostsFirstName() throws Exception {
this.mockMvc.perform(post("/post")
.param("firstName", "jon")
.param("title", "devops")
.param("link", "jon.devops"))
.andDo(print())
.andExpect(status().isOk());
}
}

Using MockMvc, we are able to perform get and post calls, pass in parameters, print out the result of the call, and verify that we get our expected 200 OK status.

Mocking Static Methods

Let me set the context here. So now you want to override the result of a static method call. But that’s not how mocking works. A mock, being a mock of a class, needs a real instance of an object.

I’m gonna go ahead and spare you. Mocking static methods as well as private methods is possible using a library called PowerMock, but the Java overlords will forever shun you for using it. The only reason PowerMock works is because it gives you the ability to mock static and private methods by manipulating the underlying bytecode. However, there is still a way to do it all with Mockito by implementing a solution similar to the first one using a wrapper method and a spy.

Spies allow us to partially mock. This means that we can mock a part of the object but allow for real method calls for the rest of the object.

The class to test:

public class UnderTest {
public String regularMethod() {
return staticMethodWrapper() + " and I am using it from UnderTest";
}

public String staticMethodWrapper(){
return DependentClass.staticMethod(); //Instead of calling the static method directly, we are wrapping it in a non static method
}
}

The DependentClass:

public class DependentClass {

public static String staticMethod(){
return "This is a static method";
}
}

Our test:

public class UnderTestTest {

private UnderTest ut;

private UnderTest spy;

@Before
public void setup() {
MockitoAnnotations.initMocks(this);
ut = new UnderTest(); // A real instance of UnderTest
spy = Mockito.spy(ut); // Spying on that real instance
}

@Test
public void staticMethodTest(){
doReturn("Static Method Override").when(spy)
.staticMethodWrapper(); // Overriding the staticMethodWrapper call on our spy.
String result = spy.regularMethod();
assertEquals(result, "Static Method Override and I am using it from UnderTest");
}
}

Here, we are creating a wrapper method that calls the static method. Now, in our regularMethod, we can call this wrapper class instead of calling the static class directly. A spy does exactly what it sounds like -- it allows us to spy on real objects. In other words, using a spy (our partial mock) enables us to override what the method returns.

Some people will tell you that you should avoid static methods at all costs since they usually lead to bad practices. I’m going to avoid that debate all together simply because, even if you have no static methods in your project whatsoever, you might still find yourself mocking a dependency that you never wrote. You can’t really control the libraries you depend on.

I’m far from a Java expert let alone a unit testing expert, but I hope that the information on mocking provided here helps you with the more challenging aspects of unit testing. Please don’t hesitate to reach out if you have any questions or comments!

Have a question or comment?
Contact uS
About Liatrio

Liatrio is a collaborative, end-to-end Enterprise Delivery Acceleration consulting firm that helps enterprises transform the way they work. We work as boots-on-the-ground change agents, helping our clients improve their development practices, react more quickly to market shifts, and get better at delivering value from conception to deployment.

Ready to Accelerate Delivery and Transform Your Organization?

Contact Us
»