模拟私有方法:
  如果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对象私有属性的值:
  我们知道EasyMock和PowerMock的Mock对象是通过JDK/CGLIB动态代理实现的,本质上是类的继承或者接口的实现,但是在java面向对象编程中,基类对象中的私有属性是无法被子类继承的,所以如果被Mock对象的方法中使用到了其自身的私有属性,并且这些私有属性没有提供对象访问方法,则使用传统的Mock方法是无法进行测试的,JMockit提供了设置Mocked对象私有属性值的方法,代码如下:
  被测试代码:
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;
}
}
  使用JMockit设置私有属性:
@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());
}
  使用JMockit设置静态私有属性:
@Test
public void testMockPrivateStaticProperty() throws IOException {
new NonStrictExpectations(Class3Mocked.class) {
{
this.setField(ClassMocked.class, "className", "className has bean change!");
}
};
assertEquals("className has bean change!", ClassMocked.getClassName());
}
  (2).基于状态的Mock方式:
  JMockit上面的基于行为Mock方式和传统的EasyMock和PowerMock流程基本类似,相当于把被模拟的方法当作黑盒来处理,而JMockit的基于状态的Mock可以直接改写被模拟方法的内部逻辑,更像是真正意义上的白盒测试,下面通过简单例子介绍JMockit基于状态的Mock。
  被测试的代码如下:
public class StateMocked {
public static int getDouble(int i){
return i*2;
}
public int getTriple(int i){
return i*3;
}
}
  改写普通方法内容:
@Test
public void testMockNormalMethodContent() throws IOException {
final StateMocked obj = new StateMocked();
new NonStrictExpectations(obj) {
{
new MockUp<StateMocked>() {//使用MockUp修改被测试方法内部逻辑
@Mock
public int getTriple(int i) {
return i * 30;
}
};
}
};
assertEquals(30, obj.getTriple(1));
assertEquals(60, obj.getTriple(2));
Mockit.tearDownMocks();
}
  修改静态方法的内容:
  若要改写静态方法内容,则首先需要新建一个类,包含与被测试静态方法相同方法签名的方法,并且使用@Mock标注,代码如下:
public class StaticMocked {
@Mock
public static int getDouble(int i){
return i*20;
}
}
  测试代码如下:
@Test
public void testDynamicMockStaticMethodContent() throws IOException {
Mockit.setUpMock(StateMocked.class, StaticMocked.class);
assertEquals(20, StateMocked.getDouble(1));
assertEquals(40, StateMocked.getDouble(2));
Mockit.tearDownMocks();
}
  JMockit和PowerMock混用时不兼容问题:
  由于PowerMock需要在单元测试类上添加@RunWith(PowerMockRunner.class)注解,用于表面使用PowerMock来执行单元测试,而JMockit不需要指定@RunWith注解,因此当一个单元测试类中混合使用PowerMock和JMockit时,JMockit总是会报错初始化失败,因此我建议不要在同一个单元测试类中混用PowerMock和JMockit。
  另外,JMockit要求必须出现在JVM classpath的中Junit前面位置,因此在添加Maven依赖时记得要把JMockit放在Junit前面,否则同样报错JMockit初始化失败。
  统计JMockit的单元测试覆盖率:
  由于JMockit使用JavaSE5中的java.lang.instrument包开发,因此一般的单元测试覆盖率统计插件和工具对其无法工作,必须要借助自带的JMockit coverage才行,对于使用Eclemma插件和maven+sonar方式的单元测试覆盖率统计,分别有如下方法:
  (1).Eclemma插件方式:
  如果直接使用Eclemma插件来统计单元测试覆盖率,会发现Eclemma长时间挂起阻塞,强行结束时会报错说找不到-javaagent等等,解决方法如下:
  在Eclipse中右键选择Coverage as ->Coverage Configurations,配置Junit的JVM参数如下:
  -javaagent:"${settings.localRepository}"/com/googlecode/jmockit/jmockit-coverage/0.999.24/jmockit-coverage-0.999.24.jar
  其中${settings.localRepository}是maven资源目录,例如:
  -javaagent:D:userdataadministrator.m2 epositorycomgooglecodejmockitjmockitcoverage.999.24jmockit-coverage-0.999.24.jar
  (2).Maven+Sonar方式:
  如果直接使用mvn sonar:sonar命令时,发现任何单元测试无法执行,长时间卡住,强行结束后再次执行时maven工程target目录下surefire目录无法删除,只有重启机器后才能删除,解决方法如下:
  在pom文件中添加如下的插件:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12</version>
<configuration>
<argLine>-javaagent:"${settings.localRepository}"/com/googlecode/jmockit/jmockit-coverage/0.999.24/jmockit-coverage-0.999.24.jar</argLine>
</configuration>
</plugin>
  再次执行mvn sonar:sonar命令可以正常统计出JMockit的单元测试覆盖率。