Yeah, Yeah I know, I know implementing functionality using static methods is bad and stuff like that. But sometimes you just can't help it as its not in your control. Hence, as all wise man (woman included) say, better deal with it!
We have not yet seen how we could mock normal classes and interface and directly looking at how to mock static methods. Well as Mr. IronMan said
Sometimes you gotta run before you can walk.
Currently, I am working on a project that uses Spring Roo. Roo uses a lot of AspetJ goodness. It adds a lot of static methods to domain objects via AspectJ, making the domain objects richer. For e.g. In a domain Class Customer it would add methods like findCustomer, findAllCustomers, countCustomers etc. If you are interested in exploring more about Spring Roo, start here.
Spring Roo uses Spring MVC as the web layer. Hence, I am going to take example of CustomerController (a controller that does CRUD on Customer domain object). Enough history, lets see the real stuff!
When user requests to see the customer information, CustomerController should find the requested Customer and return that in the model.
The controller code would look like
Not showing the Spring MVC specific annotations. Here, findCustomer is the method added by Spring Roo to the Customer domain class. This is a static method which is invoked by CustomerController to get the requested Customer instance. This is what we want to test.
How will we test it - How do they do it!:
As I explained in my previous post, PowerMocks uses custom class-loader and byte-code manipulation to enable mocking of static methods. PowerMocks does this by using the following annotations
- @RunWith -- PowerMocks uses a custom Runner called PowerMockRunner. This is done so that
- PowerMocks could show informative message if an exception is thrown from the test
- PowerMocks could notify listeners of certain events. For e.g. before the test methods start executing an event BeforeTestMethod is triggered. Don't worry, if you don't understand this. Its a little advanced stuff. Just remember, that we need to use a Custom runner.
- @PrepareForTest -- This annotation tells PowerMock to prepare certain classes for testing. Classes needed to be defined using this annotation are typically those that needs to be byte-code manipulated. This includes final classes, classes with final, private, static or native methods that should be mocked
We are saying PowerMocks (using the Mockito extension api) that we want to mock static methods in Customer class.
Next up, we have to stub the method we are interested in (we will be doing state based testing. PowerMocks also supports interaction based testing, using the verify syntax. Will not be showing it in this post though). That's done like this
And then we can simply assert the customer is returned in the modelMap. That's all! Here's the complete test case code.
And we are done! As we can see, PowerMocks (using the Mockito extension api) uses English like syntax, because of this test cases look a lot compact and are easier to read.
As usual, feedback, comments and complaints are always welcome!