Writing tests for an unknown code is either difficult or tiresome; difficult, if I decide to understand large chucks of the project before writing a lick of code; tiresome, if I chose to only understand the module that I am changing and just mock the rest of classes when unit testing. In reality, It should be a bit of both.
In the past I made lots and lots of mock objects by making classes that implement a certain interface or try to instantiate a class with lots of dependency, when really, I only care about one or two methods in the class. It's a tiresome, verbose, and just plain an ugly thing to do for writing a unit test.
Fortunately, I came across Mockito with allows you to mock and stub classes effortlessly.
Take the
Say I want to make a change to the
Before Mockito, I would need to instantiate an instance of
What is it that I really need in order to test this code? It's the logic in
This is exactly what Mockito give me! Here is an example
The key thing to notice here is the
That's it! My unit test is concise/readable, free from a bunch of setup code. I can always setup a different list of results to test against in my unit test.
It makes writing unit tests fun versus a necessary evil.
StatsManager
class that has a genderCount()
method.
public class StatsManager {
private JDBCTemplate template;
public long genderCount(Gender gender) {
//perform some business logic
...
}
public void setJDBCTemplate(JDBCTemplate template) {
this.template = template;
}
...
}
Say I want to make a change to the
genderCount()
method and I need an unit test to validate my change.Before Mockito, I would need to instantiate an instance of
JDBCTemplate
. It happens that JDBCTemplate
needs an javax.sql.DataSource
instance to work. I would then try to make a mock class of this interface, but then it turns out that javax.sql.DataSource
has a dependency javax.sql.Connection
so I have to mock that class and world goes round and round... but I never finishing mocking!What is it that I really need in order to test this code? It's the logic in
genderCount()
that I want to test, and don't want to concern myself with all these other setup classes/interfaces. Infact, I know that queryAsList(String sql)
is the only method called on the JDBCTemplate
class in StatsManager.genderCount()
. All I want is a certain List of results when genderCount()
is called.This is exactly what Mockito give me! Here is an example
public class StatsManagerTest {
@Test
public void testGenderCount() {
StatsManager manager = new StatsManager();
JDBCTemplate template = mock(JDBCTemplate.class)
List results = new ArrayList();
//initialize list.
...
when(template.queryAsList(anyString())).thenReturns(results);
long maleCount = template.genderCount(Gender.MALE);
//Assert a bunch of stuff
...
long femaleCount = template.genderCount(Gender.FEMALE);
//Assert a bunch of stuff
...
}
}
The key thing to notice here is the
when()
and thenReturns()
pair. What this is doing is that when queryAsList()
is called regardless of what string is passed as a parameter, return this fixed list of results.That's it! My unit test is concise/readable, free from a bunch of setup code. I can always setup a different list of results to test against in my unit test.
It makes writing unit tests fun versus a necessary evil.
3 comments:
Instead of mocking the complete JDBCTemplate type, you could also mock JDBC at a lower level using jOOQ's mocking capabilities as described in this blog post:
http://blog.jooq.org/2013/02/20/easy-mocking-of-your-database/
With that technique, you'll only have to implement one MockDataProvider method, instead of all of the Methods from JDBCTemplate.
Sorry Lukas, but unless i am wrong in some way, it is not possible to mock jdbc at the lowest level using jOOQ.
Actually the data provider is not the only thing you have to implement. You also have to create Records. And that becomes a nightmare when testing code with no database at all, and if you don't want to generate code.
There is no public accessible Record implementation one can use or extend easily, without generating code.
Note : I was trying this in an application without jOOQ. I just wanted to test my dao by mocking jdbc.
Sorry for the late reply. Perhaps it's difficult, but I don't see how it would be easier with JDBC directly...
Note, we're constantly adding new API to generate Result or Record objects...
Post a Comment