Mockito和JMockit的对比:https://www.sibida.vip/article/126324

Mockito、JMockit、EasyMock、PowerMock、jMock、Unitils对比 :

简介

jmockit用来写ut,即单元测试。

中文官网:http://jmockit.cn/showChannel.htm?channel=1

关于单元测试ut、功能测试ft、系统测试st、验收测试uat:

关于官网没有提到的,个人的经验:

  1. 提示期待和被期待的对象不同,但对比结果相同,如果不是必须同一个实例,可能是"null"在控制台打印的是null,null在控制台也打印的是null,可能是1L和1都显示1,使用BeyondCompare等对比工具发现不了。
  2. 控制台打印的被期待的逻辑是测试中写的,实际的逻辑是被测试的代码。
  3. @Mocked用在ut的变量里。
  4. Spring Test中提供了一些模拟的对象可以方便地使用。如:MockHttpServletRequest、MockHttpServletResponse、MockMultipartFile、MockFilterChain
<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.14.RELEASE</version>
        </dependency>
  1. ut类中被注入的成员变量,也就是加了@Injectable的成员变量,是被jmockit自己实现,个人修改后,除非通过测试方法传参进去,否则不会生效。不会传参进去,又想自定义这个成员变量,只能自己写一个实现类,然后用jmockit提供的FieldReflection.setFieldValue(field, targetObject, value),它会通过反射将ut执行时的被测试类的成员变量改了,mocked后赋值、injectable后赋值、用mockup都不能解决问题。而且必须在测试方法执行前,不能写在Junit的@Before里,此时@Tested的变量还没有实例化;field可以被测试类.class.getDeclaredField(变量名)得到,targetObject就是@Tested的变量,value就是自己写的被测试类的成员变量的实现(接口实现或子类)。
  2. 被测试类里用到了自己,也就是注入了自己,此时,被测试方法内部用了自己,就会有NPE,也只能通过FieldReflection.setFieldValue解决。
  3. 被测试类里@Autowired方式注入或者构造方法的方法注入,都用@Injectable。
  4. coverage插件已经安装,打开工程后没有coverage窗口,coverage运行按钮是灰色的,重新打开工程可以解决。
  5. 在new Expectations里用jmockit的withInstanceLike方法,传入的参数不需要内容完全相同,也可以继续向下执行,可以结合将实例赋值来解决mapper执行insert后得到自增ID的场景。withInstanceOf只需传入参数类型就行。
  6. 将实例赋值,传递下去,实例对应的类不能是序列化的类:
XXX xxx = new XXX();
new Expectations(XXX.class){
  {
    new XXX();
    result = xxx;
  }
};
  1. 解决TimeUtils:
new Expectations(TimeUtils.class){
  {
    TimeUtils.currentTimeSecs();
    result = 1;
  }
};
  1. System的currentTimeMillis是native方法,不能像TimeUtils一样解决,需用MockUp,MockUp对当前所在方法内涉及到的所有实例生效:
new MockUp<System>() {
            @Mock
            public long currentTimeMillis() {
                return 1;
            }
        };
  1. BufferedWriter、FileWriter等涉及到文件,也可以用MockUp解决,因为文件可能在服务器上,ut不依赖外部资源,所以不想报文件找不到,不想读写文件;java的mock对象私有属性赋值_测试类clinit可以模拟静态代码块http://jmockit.cn/showChannel.htm?channel=4:
new MockUp<BufferedWriter>() {
            @Mock
            public void close() {
            }

            @Mock
            public void flush() {

            }
        };
new MockUp<FileWriter>() {
            @Mock
            public void $init(File file, boolean append) {
            }

            @Mock
            public void close() {
            }
        };
new MockUp<FileUtils>() {
            @Mock
            public void writeByteArrayToFile(File file, byte[] data) {
            }
        };
  1. 涉及到多线程的测试,模拟的方法被执行的次序需要固定,否则Expectation不能执行成功,所以需要将被测试类的多线程模拟成单线程执行:
List<Runnable> runnableList = new ArrayList<>();
new MockUp<ThreadPoolExecutor>(){
    @Mock
    public Future<?> submit(Runnable task){
        runnableList.add(task);
        // 2是任务的个数,将任务都存起来,存完后再依次执行,如果模拟时线程个数是外部传入的,可以直接传入1,多线程改单线程
        if(runnableList.size() == 2){
            runnableList.forEach(Runnable::run);
        }

        return null;
    }
};
  1. 如果MockUp使用时,报mockit.internal.ClassFile$NotFoundException: Unable to find class file for,需调整MockUp的使用顺序,如:
new MockUp<URLClassLoader>() {
    @Mock
    public InputStream getResourceAsStream(String name) {
        return null;
    }
};
new MockUp<FileUtils>(){
    @Mock
    public void writeByteArrayToFile(File file, byte[] data){
    }
};
// 会报:mockit.internal.ClassFile$NotFoundException: Unable to find class file for,交换一下顺序就能解决
  1. 一个类的静态成员变量实例化时抛出异常,想不抛出,可以用FieldReflection.setFieldValue,第二个参数传null就行。
  2. 被测试类的私有方法不被公开的调用,还想测试一下,可以用jmockit提供的MethodReflection.invoke反射执行。

其它:
18. IDEA的被测试类中,Mac是Command+Shift+T,Windows是Ctrl+Shift+T,然后回车,可以看到已有的测试类,也可以生成新的测试类:选择生成策略。
19. 在目录上可以右键Run … with coverage可以查看类、方法、行的覆盖率。
20. 运行ut后,就可以编辑下一个ut方法了,毕竟ut执行前jmockit需要执行一些代码。 开始运行后,编辑不会影响运行,可以节省ut编写时间,不要问我怎么知道的,当要覆盖37000行时,节省出来的时间很重要。
21. IDEA的Settings里搜Coverage,勾选Add to the active suits可以将目前的ut执行的Coverage合并到上一次执行的Coverage里,可以实时了解当前大目录的Coverage。
22. 可以用Statistic插件先看一下代码行集中在哪些目录,哪些类,可以更快地提升行覆盖率。
23. 被测试类的方法声明改测试类的同一个方法声明,IDEA里用正则替换:
public\s.\s(.)(.*)\s{0,1}{
public void $1() {
24. 测试类的方法统一test开头,下一个字母大写,IDEA里用正则替换:
@Test
public void ([a-su-z])
@Test
public void test\u$1
25. 如果被测试的类实例化时报错,可以在静态代码块中给被测试类的实例传入正确的参数。

补充:
jmockit教程_jmockit使用总结-MockUp重点介绍:
Java服务端单元测试指南: