jmockit
Mocking with Jmockit
JMockit 是用以帮助开发人员编写测试程序的一组工具和API,该项目完全基于 Java 5 SE 的 java.lang.instrument 包开发,内部使用 ASM 库来修改Java的Bytecode。
在单元测试中,经常需要进行一些mock操作。现在已经有了一些比较不错的框架在做这些事情,比如:EasyMck,他在大多数情况下运行良好,但是对于某些结构的设计却显得无能为力。
EasyMock等众多的mock框架仅能mock一些public,non static or final的方法,在大多数情况下这并没有什么问题,他可以处理大多数的问题,但是当测试的代码包含了一些静态方法,可能就让问题变得难以解决,有一种选择即是重构他(过多的静态方法可能预示着这并不是一个很好的设计),但是当你使用外部引用库所提供的方法,问题又该如何解决呢?
JMockit是一个能帮我们解决以上问题的轻量级框架,他允许你动态的改变已有的方法,这主要基于java 1.5的Instrumentation框架,这样便可以使得JMockit能够适应几乎所有的设计。他允许你重定义private,static and final方法,甚至是no-arg constructors都能够并轻易的重定义。
在实际的项目中有些方法可以重定义而有些不行,为了更好的说明如何对方法进行重定义,下面有一个简单的类和对应测试代码的demo,他尽可能考虑到了几乎所有的情况,供大家方便的学习。
ClassToMock :
publicclassClassToMock {
privateStringmemberToSet;
privatestaticStringstaticMember;
static{
staticMember= "Static initialized";
}
publicClassToMock() {
this.memberToSet= "Member set by original constructor";
}
publicClassToMock(String value) {
this.memberToSet= "Member set by original constructor";
}
publicString publicMethod() {
return"Original public method";
}
protectedString protectedMethod() {
return"Original protected method";
}
String defaultMethod() {
return"Original default method";
}
publicString methodThatUsesPrivateMethod() {
returnprivateMethod();
}
privateString privateMethod() {
return"Original private method";
}
publicString getMemberToSet() {
returnmemberToSet;
}
publicString getStaticMember() {
returnstaticMember;
}
}
ClassToMockTest :
importjunit.framework.TestCase;
importmockit.Mockit;
publicclassClassToMockTestextendsTestCase {
privateClassToMock mockedClass;
publicstaticclassReplacement {
static{
}
publicReplacement() {
}
publicReplacement(String test) {
}
publicString publicMethod() {
return"Replaced public method";
}
publicString protectedMethod() {
return"Replaced protected method";
}
publicString defaultMethod() {
return"Replaced default method";
}
publicString privateMethod() {
return"Replaced private method";
}
}
protectedvoidsetUp()throwsException {
Mockit.redefineMethods(ClassToMock.class, Replacement.class);
mockedClass =newClassToMock("test");
}
protectedvoidtearDown()throwsException {
Mockit.restoreAllOriginalDefinitions();
}
/**
* Public methods can be replaced
*/
publicvoidtestReplacePublic() {
assertEquals("Replaced public method", mockedClass.publicMethod());
}
/**
* Protected methods can be replaced.
* The replacement method should be declared public however
*/
publicvoidtestReplaceProtected() {
assertEquals("Replaced protected method", mockedClass.protectedMethod());
}
/**
* Package accessable methods can be replaced
* The replacement method should be declared public however
*/
publicvoidtestReplaceDefault() {
assertEquals("Replaced default method", mockedClass.defaultMethod());
}
/**
* Private methods can be replaced
* The replacement method should be declared public however
*/
publicvoidtestReplacePrivate() {
assertEquals("Replaced private method", mockedClass
.methodThatUsesPrivateMethod());
}
/**
* Non-default constructors can be replaced
* Your mock definition must have a default constructor however
*/
publicvoidtestReplaceConstructor() {
assertEquals(null, mockedClass.getMemberToSet());
}
/**
* Default constructors <b>can't</b> be replaced
*/
publicvoidtestReplaceDefaultConstructor() {
mockedClass =newClassToMock();
assertEquals("Member set by original constructor", mockedClass
.getMemberToSet());
}
/**
* Static initializers <b>can't</b> be replaced
*/
publicvoidtestReplaceStaticBlock() {
assertEquals("Static initialized", mockedClass.getStaticMember());
}
}