基于springmvc的Junit与Jmockit使用

时间:2024-04-11 07:47:18

JDK1.6只能支持Jmockit1.40一下的版本,本人的项目为JDK1.7使用Jmockit1.44版本
整合Jmockit时要注意Juntil与Jmockit的编译时的顺序,如果为maven项目则在pom.xml中先写jmockit依赖再写junit的,若果是非maven项目,则在引入的jar包中调整顺序如下图
基于springmvc的Junit与Jmockit使用
否则运行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了,如果上面有啥错误欢迎请指出,有疑问欢迎留言一起探讨学习。