JDK1.6只能支持Jmockit1.40一下的版本,本人的项目为JDK1.7使用Jmockit1.44版本
整合Jmockit时要注意Juntil与Jmockit的编译时的顺序,如果为maven项目则在pom.xml中先写jmockit依赖再写junit的,若果是非maven项目,则在引入的jar包中调整顺序如下图
否则运行jmockit的用例时会报错如下:java.lang.ExceptionInInitializerError…
Caused by:java.lang.IllegalStateException:JMockit didn’t get initialized;please check jmockit.jar precedes junit.jar in the classpath…
Jmockit几种常用注解:
@Mocked
可以修饰一个类或者接口,被修饰的类将会生成一个全新的对象,原来的类里的逻辑将被mock掉,类的方法返回的都是默认值(即比如原先该类里的方法需要返回一个String的值,那么被mock掉之后就直接返回一个默认值null了),该注解范围较广较少使用;
@Tested 和 @Injectable
这两个注解是好基友,搭配使用,@Tested 注解的类是测试类,该测试类如果内部引用其他依赖(依赖某某service接口等),这时候就需要@Injectable 全部注入进来,因为被@Tested注解的类实例化时会去找@Injectable注解的依赖生成对象,注意,此时被@Injectable的依赖它原本的方法也是不好使了的,如果没有录制期望,那就全部返回默认值,看下面的例子吧
被测试的Controller类
@Controller
@RequestMapping(value="/")
public class DmController {
@Autowired
private DmService dmService; //服务接口依赖
@Autowired
private EmailService emailService;//发邮件的接口依赖
//保存操作
@RequestMapping(value="newdm.do",method = RequestMethod.POST)
@ResponseBody
public Object newdm(HttpSession session, DmDTO dmDTO) {
User user = getUser(session);
String name = user.getName();
String email = emailService.sendEamil("[email protected]");//调用接口发邮件,成功返回ok
if(name != null && email.equals("ok")) {
String result = dmService.save(dmDTO);//假设用户字符<5保存成功
return Message(String); //Message用于向前端返回数据对象
}
return null;
}
...
}
测试用例DmControllerTest
//测试类,需要继承几篇前整合好的BaseJunit
public class DmControllerTest extends BaseJunit{
@Tested
DmController dmController; //这里使用@Tested注解该类,该类就是被测试类,该类里所有的依赖都需要通过@Injectable方式注入,如果不全部用该方式注入则无法生产新的bean,如下
@Injectable
private DmService dmService; //服务接口依赖
@Injectable
private EmailService emailService;//发邮件的接口依赖
@Test
public void testNewdm() {
new Expectations(){ //录制期望,在这里就是录制EmailService里的sendEmail(String email)方法
emailService.sendEmail(anyString); //方法里传的是String所以可以写成anyString,
//假设该方法是这样:String sendEmail(User user);User为对象,则录制时可以写成emailService.sendEmail((User)any);
result = "ok"; //这里就是期望返回的结果,那么在被测的类里只要调用到了发邮件的方法都会返回"ok"
};//注意这里有个";"
DmDTO dmDTO = new DmDTO();
dmDTO.setName("hao"); //设置长度小于5,应该返回保存成功
Message result =(Message) dmController.newdm(session,dmDTO); //这里通过被@Tested注解的类生成的对象调用newdm(...)方法
assertEquals("保存成功",result.getinfo()); //返回的对象获取里面的信息
}
注意:看发邮件的接口如下,没有录制的方法返回的都是默认值,同样的没有录制任何方法的DmService接口,将返回的对象里所有的方法结果都是初始值。
public Interface EmailService {
String sendEmail(String email);//发邮件的方法,在上面录制了返回“ok”,那么这个方法都将会返回“ok”
String getEmailByName(String name);//该方法没有录制,都将返回String的初始值null
}
由此可见,只要用了@Tested注解的类,它里面所有的方法都要通过@Injectable注入,而注入的依赖如果没有录制期望,那么都将返回初始值。
@Capcuring
该注解主要用于接口方法的mock
还是上面的被测试类DmController,测试用例如下
public class DmControllerTest extends BaseJunit{
@Autowired
DmController dmController;//注意这里的注解,直接从spring容器中拿出DmController 的对象,而不想@Tested再去生成一个新的对象
@Capturing
private EmailService emailService;//这里是需要mock的接口依赖,其他不需要mock的依赖不需要做注入
@Test
public void testNewdm() {
new Expectations(){ //录制期望,在这里就是录制EmailService里的sendEmail(String email)方法,同样的,该接口里没有录制的方法都将返回初始值
emailService.sendEmail(anyString);
result = "ok"; //这里就是期望返回的结果,该被测试类运行后只要用到这个方法都将返回“ok”
};
DmDTO dmDTO = new DmDTO();
dmDTO.setName("hao"); //设置长度小于5,应该返回保存成功
Message result =(Message) dmController.newdm(session,dmDTO); //这里直接用spring里拿出来的对象调用newdm(...)
assertEquals("保存成功",result.getinfo()); //返回的对象获取里面的信息
}
我们知道,上面的这几个注解都有一个共性,只要被mock的接口或者类,它里面的方法如果没有被录制期望都将返回默认值,这样一来比如一个service里有多个方法,那些没有录制的方法就不能使用原来的逻辑了,所以接下来介绍的这个方法不仅解决了这个问题,而且可以跨层mock,比如测试controller层的方法,可以直接mock到dao层的方法或者mock源码里的方法都没问题,那就是mockUp
@MockUp 和@Mock
这两个注解搭配使用,大部分的需求都可以满足,功能强大,使用也更方便
还是上面的例子
public class DmControllerTest extends BaseJunit{
@Autowired
DmController dmController;//注意这里的注解,直接从spring容器中拿出DmController 的对象
//这个例子还是mock发邮件的方法,但这里不需要再注入EmailService
@Test
public void testNewdm() {
new MockUp<EmailServiceImpl>(EmailServiceImpl.class){ //注意,这里只能放类不能放接口,查看源码可以知道MockUp的有参构造方法会判断传入的参数是否为接口
@Mock
String sendEmail(String email){ //这里直接把需要mock的方法拿出来就可以,而没有mock的方法String getEmailByName(String name){...}不受影响,会按原有的逻辑返回,而不是初始值了。
return "ok"; //返回期望的结果,这里除了返回String类型,可以根据方法的返回值返回,可以看下面这个例子
}
};
//Map<String,Object>() map = new HashMap();
//map.put("key1",2);
//new MockUp<User>(){ //括号里也可以不加User.class,这样就是调用另外一个构造函数,不影响结果
// @Mock
// Map<String,Object> getUser(String userid){
// return map; //这样我想期望返回一个map,那在前面我事先构造好在这里返回即可
// }
// };
DmDTO dmDTO = new DmDTO();
dmDTO.setName("hao"); //设置长度小于5,应该返回保存成功
Message result =(Message) dmController.newdm(session,dmDTO); //这里直接用spring里拿出来的对象调用newdm(...)
assertEquals("保存成功",result.getinfo()); //返回的对象获取里面的信息
}
如果对于那些不知道事先类的接口怎么办呢,接下来方法就可以解决这个问题,假如我就想直接mock邮件接口的方法而不是实现类
public class DmControllerTest extends BaseJunit{
@Autowired
DmController dmController;
@Test
public void testNewdm() {
new MockUp<T extends EmailService>(){ //使用泛型直接继承EmailService接口,是的,这里使用extends而不是implements
@Mock
String sendEmail(String email){ //这里是直接mock接口的方法,而不再是它的实现类
return "ok"; //返回期望的结果
}
};
DmDTO dmDTO = new DmDTO();
dmDTO.setName("hao"); //设置长度小于5,应该返回保存成功
Message result =(Message) dmController.newdm(session,dmDTO); //这里直接用spring里拿出来的对象调用newdm(...)
assertEquals("保存成功",result.getinfo()); //返回的对象获取里面的信息
}
总结:现实使用中,用得最多的就是@MockUp 和@Mock了,如果上面有啥错误欢迎请指出,有疑问欢迎留言一起探讨学习。