Friday, February 15, 2008

JMock and how to mock static function and final class?

Unit test is one of the "must" task when we develop software. One of the most technology we usually used is mock. As i already said in one of my previous post,
mock is the powerful technology but they have some limitation. This limitation is depend on which type of mock object( JMock, AMock, NMock ...) or the environment.
For example, if you want to test some DAO class which directly access to DBMS, you can not create mock for all JDBC class to do unit test. And in this case, we also can not do unit test because the test depend on the environment(we have to set up an DBMS just for do unit test). Therefore, we have to chose the most suitable way to test: System test, module test, review code ...
JMock like others mock object, has it own limitation. It can not mock final class, static method. It is because of its implementation. For final class, JMock can not inherit the class to create new mock class ==> can not mock this class.
With static method, because static method can not be overriden, so JMock has no way to create mock object.

So in this case, we have to think about design and test first before code
1. Case: our method use final class.

We will use pattern template method to separate code which contain instances of final class.


class OurClass {
function myFunc() {
my code
......
URL url = new URL(href); //Can not mock URL because of final class
url.openstream(...)
....
my code


}
}
Our mission is test this function.


In this case, we create new class calls MockHelperTest and a factory MockHelperFactory



IHelperFactory = interface{
IHelperTest getMockHelper;
}

MockHelperTest = class implements IHelperTest{

void funcHelper() {
///Prepare data , result and code for mock
}
}

and we change our class to:


class OurClass {
public IHelperFactory factory = null; //factory can be set by unit test

//If we test this function, we will
//pass mock factory to this class and
//function will call mock function
//instead of real code.
//If we want to to do real code,
//just pass null factory, and it will
//create real factory and then myFunc
//will call funcMockHelper which call
//real code:
//URL url = new URL(href); //Can not mock URL because of final class
//url.openstream(...)

OurClass(IHelperFactory mockFactory) {
if mockFactory == null ) {
factory = new RealFactory();
} else {
factory = mockFactory;
}
}

function myFunc() {

my code
......
IHelperTest helper = factory.funcHelper().getMockHelper();
helper.funcMockHelper();
....
my code
}
}



And now, we can easily replace the code which can not mock which the mock code.
The same pattern for static function.

Conclusion: Think about test + design first and then code.

No comments:

Google