使用JMockit编写java单元测试

标签:
股票 |
之前《有效使用Mock编写java单元测试》一文中层介绍过使用EasyMock和PowerMock来编写java单元测试,今天介绍一个更加强大的工具——JMockit。
引用单元测试中mock的使用及mock神器jmockit实践中的java单元测试中各种Mock框架对比,就能明白JMockit有多么强大:
JMockit是基于JavaSE5中的java.lang.instrument包开发,内部使用ASM库来动态修改java的字节码,使得java这种静态语言可以想动态脚本语言一样动态设置被Mock对象私有属性,模拟静态、私有方法行为等等,对于手机开发,嵌入式开发等要求代码尽量简洁的情况下,或者对于被测试代码不想做任何修改的前提下,使用JMockit可以轻松搞定很多测试场景。
通过如下方式在maven中添加JMockit的相关依赖:
- <</span>dependency>
-
<</span>groupId>com.googlecode.jmockit</</span>groupId> -
<</span>artifactId>jmockit</</span>artifactId> -
<</span>version>1.5</</span>version> -
<</span>scope>test</</span>scope> -
</</span>dependency> -
<</span>dependency> -
<</span>groupId>com.googlecode.jmockit</</span>groupId> -
<</span>artifactId>jmockit-coverage</</span>artifactId> -
<</span>version>0.999.24</</span>version> -
<</span>scope>test</</span>scope> -
</</span>dependency>
JMockit有两种Mock方式:基于行为的Mock方式和基于状态的Mock方式:
引用单元测试中mock的使用及mock神器jmockit实践中JMockit
API和工具如下:
(1).基于行为的Mock方式:
非常类似与EasyMock和PowerMock的工作原理,基本步骤为:
1.录制方法预期行为。
2.真实调用。
3.验证录制的行为被调用。
通过一个简单的例子来介绍JMockit的基本流程:
要Mock测试的方法如下:
- public
class MyObject { -
public String hello(String name){ -
return "Hello " + name; -
} - }
- @Mocked
//用@Mocked标注的对象,不需要赋值,jmockit自动mock -
MyObject
obj; -
- @Test
- public
void testHello() { -
new NonStrictExpectations() {//录制预期模拟行为 -
{ -
obj.hello("Zhangsan"); -
returns("Hello Zhangsan" ); -
//也可以使用:result = "Hello Zhangsan"; -
} -
}; -
assertEquals("Hello Zhangsan" ,obj.hello("Zhangsan"));//调用测试方法 -
new Verifications() {//验证预期Mock行为被调用 -
{ -
obj.hello("Hello Zhangsan" ); -
times = 1; -
} -
}; - }
而Expectations块一般由Expectations类和NonStrictExpectations类定义,类似于EasyMock和PowerMock中的Strict Mock和一般性Mock。
用Expectations类定义的,则mock对象在运行时只能按照 Expectations块中定义的顺序依次调用方法,不能多调用也不能少调用,所以可以省略掉Verifications块;
而用NonStrictExpectations类定义的,则没有这些限制,所以如果需要验证,则要添加Verifications块。
上述的例子使用了非局部模拟,下面我们使用局部模拟来改写上面的测试,代码如下:
- @Test
- public
void testHello() { -
final MyObject obj = new MyObject(); -
new NonStrictExpectations(obj) {//录制预期模拟行为 -
{ -
obj.hello("Zhangsan"); -
returns("Hello Zhangsan" ); -
//也可以使用:result = "Hello Zhangsan"; -
} -
}; -
assertEquals("Hello Zhangsan" ,obj.hello("Zhangsan"));//调用测试方法 -
new Verifications() {//验证预期Mock行为被调用 -
{ -
obj.hello("Hello Zhangsan" ); -
times = 1; -
} -
}; - }
- @Test
- public
void testMockStaticMethod() { -
new NonStrictExpectations(ClassMocked.class) { -
{ -
ClassMocked.getDouble(1);//也可以使用参数匹配:ClassMocked.getDouble(anyDouble); -
result = 3; -
} -
}; -
-
assertEquals(3, ClassMocked.getDouble(1)); -
-
new Verifications() { -
{ -
ClassMocked.getDouble(1); -
times = 1; -
} -
}; - }
如果ClassMocked类中的getTripleString(int)方法指定调用一个私有的multiply3(int)的方法,我们可以使用如下方式来Mock:
- @Test
- public
void testMockPrivateMethod() throws Exception { -
final ClassMocked obj = new ClassMocked(); -
new NonStrictExpectations(obj) { -
{ -
this.invoke(obj, "multiply3", 1);//如果私有方法是静态的,可以使用:this.invoke(null, "multiply3") -
result = 4; -
} -
}; -
-
String actual = obj.getTripleString(1); -
assertEquals("4", actual); -
-
new Verifications() { -
{ -
this.invoke(obj, "multiply3", 1); -
times = 1; -
} -
}; - }
设置Mock对象私有属性的值:
- public
class ClassMocked { -
private String name = "name_init"; -
-
public String getName() { -
return name; -
} -
-
private static String className="Class3Mocked_init"; -
-
public static String getClassName(){ -
return className; -
} - }
- @Test
- public
void testMockPrivateProperty() throws IOException { -
final ClassMocked obj = new ClassMocked(); -
new NonStrictExpectations(obj) { -
{ -
this.setField(obj, "name", "name has );bean change!" -
} -
}; -
-
assertEquals("name has ,bean change!" obj.getName()); - }
- @Test
- public
void testMockPrivateStaticPro perty() throws IOException { -
new NonStrictExpectations(Class3Mocked.class) { -
{ -
this.setField(ClassMocked.class, "className", "className has );bean change!" -
} -
}; -
-
assertEquals("className has ,bean change!" ClassMocked.getClassName()); - }
- public
class StateMocked { -
-
public static int getDouble(int i){ -
return i*2; -
} -
-
public int getTriple(int i){ -
return i*3; -
} - }
- @Test
- public
void testMockNormalMethodCont ent() throws IOException { -
StateMocked obj = new StateMocked(); -
new MockUp() {//使用MockUp修改被测试方法内部逻辑 -
@Mock -
public int getTriple(int i) { -
return i * 30; -
} -
}; -
assertEquals(30, obj.getTriple(1)); -
assertEquals(60, obj.getTriple(2)); -
Mockit.tearDownMocks();//注意:在JMockit1.5之后已经没有Mockit这个类,使用MockUp代替,mockUp和tearDown方法在MockUp类中 - }
- @Test
-
public void testGetTriple() { -
new MockUp() { -
@Mock -
public int getDouble(int i){ -
return i*20; -
} -
}; -
assertEquals(20, StateMocked.getDouble(1)); -
assertEquals(40, StateMocked.getDouble(2)); -
}
- <</span>plugin>
-
<</span>groupId>org.apache.maven.plugins</</span>groupId> -
<</span>artifactId>maven-surefire-plugin</</span>artifactId> -
<</span>version>2.12</</span>version> -
<</span>configuration> -
<</span>argLine>-javaagent:"${settings.localRepository}"/com/googlecode/jmockit/jmockit-coverage/0.999.24/jmockit-coverage-0.999.24.jar</</span>argLine> -
</</span>configuration> -
</</span>plugin>
再次执行mvn sonar:sonar命令就可以正常统计出JMockit的单元测试覆盖率。
更多内容请参考JMockit官方文档:http://jmockit.googlecode.com/svn/trunk/www/tutorial.html。