文章目录
前言:想编写零缺陷的代码,编写测试代码很关键。测试代码的有效性和可靠性也是非常重要的,因此我在最近编写了一点点测试代码之后汇总了“亿”点点的实用技巧。
一、如何访问到类的私有方法或属性
1.1如果类可以构造实例
假设我们有个类Student有私有方法和私有成员变量如下:
class Student{
private String hobby;
private String readBook(String book){
return "happiness";
}
}
那么,怎样在单元测试代码中通过jmock方便地访问到它们呢?大家第一想到的是使用反射来访问到它们:
私有方法的还有如下的方法:
Method method= Student.class.getDeclaredMethod("readBook",String.class);
method.setAccessible(true);
method.invoke(student, "aBook");
补充说明:
- 如果以上测试的函数参数非具体的对象如String,为父类的List/Map等,我们只需要传入List.class/Map.class即可;
- 记得getClass()跟XX.class是不一样,记得不要用错了。
除了直接使用反射,还可以使用
Deencapsulation这样优雅去调用(可以更加方便地获取返回值而且无需处理异常捕获,并进行Assert判定)
import mockit.Deencapsulation;
Student student = new Student();
//其中,“EnglishBook”是传入给readBook()方法的参数,gains是readBook()方法的返回值。
String hobby = Deencapsulation.invoke(student, "hobby");
String gains =Deencapsulation.getField(student, "readBook", "EnglishBook");
Assert.assertEquals("happiness", gains);
1.2如果类没法实例化的情况
class Computer{
private Computer(){
)
private static String surf(String book){
return "happiness";
)
这种情况也可以使用反射和Deencapsulation进行获取私有方法
Method functionName=AUtil.class.getDeclaredMethod("functionName", String.class, String.class);
functionName.setAccessible(true);
functionName.invoke(AUtil.class,"id","name");//没有正常获取返回值
String gain = Deencapsulation.invoke(Computer.class, "surf", "book");
Assert.assertEquals("happiness", gains);
二、如何mock类的构造器
首先为什么要mock类的构造器呢?由于有些业务场景下类的构造器比较复杂没那么好构造时,就非常需要必要将构造器mock掉
方法如下:
new MockUp<NeedToMockClass>{
@Mock
public void $init(){
}
@Mock
public coid $init(String[] args){
}
}
接下来,再列举实际的场景
现有需要mock的对象如下:
mock的场景如下:编写测试代码的时候需要聚焦到函数generateResBeans的代码段1和2,Resolver这个对象及其函数build是我们不关注的(在其他的测试类中覆盖),需要将其构造器mock掉
public List<ResBean> generateResBeans(Model model){
//...code segment 1
Resolver resolver= new Resolver(model);
List<Res> resList = resolver.build();
//...code segment 2
}
因此在测试代码使用
public void should_success_when_build_successfully() throws Exception {
new MockUp<Resolver>(){
@Mock
public void $init(Model model){
this.model = model;
}
@Mock
public List<Res> build(){
return new ArrayLIst<>();
}
}
Assert.assertTrue(generateResBeans(new Model("model")).isEmpty());
}
三、如何mock类的私有属性
3.1如果类属性为非static
使用Whitebox.setlnternalState()对需要mock 的类对属性进行处理, 现有需要mock的类对象如下,其中需要mock调用其属性logger
public class Impl{
private MyLog logger = AppLog.getLogger();
public MyLog logger1 = AppLog.getLogger();
)
测试代码如下:
import org.mockito.internal.util.reflection.Whitebox;
OssLog osslog = MOckito.mock(OssLog.class);
Impl impl = new Impl();
Whitebox.setInternalState(impl, "logger", ossLog);
Whitebox.setInternalState(impl, "logger1", ossLog);
impl.function(context,null);//验证function会不会打印
Mockito.verify(ossLog,Mockito.never()).error(anyString()));
3.2如果类属性为static
这种没有直接使用Whitebox.setlnternalState()去设置,用法如下:
static void setFinalStatic(Field field, Object newValue) throws Exception{
field.setAccessible(true);//remove final moidfer from field
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() &~Modifier.FINAL);
field.set(null, newValue);
}
// 如果该属性为非final,则使用如下方法
static void setStatic(Field field, Object newValue) throws Exception{
field.setAccessible(true);
field.set(null, newValue);
}
现有需要mock的类对象如下,其中需要mock调用其属性logger
public class Impl{
private static final MyLog logger = AppLog.getLogger();
)
测试代码如下:
import static org.mockito.Matchers.anyString;
OssLog ossLog=Mockito.mock(OssLog.class);
Impl impl =new Impl0);
setFinalStatic(Impl.class.getDeclaredField("logger"), ossLog);
impl.function(context,null);//验证function会不会打印
Mockito.verify(ossLog,Mockito.never()).error(anyString()));
四 verify校验函数是否执行
4.1如果函数为非静态的而且类对象可mock
先mock对象,然后进行mock对象的执行
JobState mock=Mockito.mock(JobState.class);
mock.setState(state);//执行业务代码
Mockito.verify(mock,times(1)).setState(state);
Mockito.verify(mock,never().setState(state);
Mockito.verify 的其他用法:
https://blog.csdn.net/blueZhangFun/article/details/103665393
4.2如果函数为非静态
Mockito很强大,但是它不支持静态方法.所以,就用Powermock了。
(以上内容为DreamKite本人原创,转载请附上原文链接)