• JMockit学习笔记


    一、在eclipse下建立JMockit工程

    1、下载最新版JMockit(jmockit-1.4.zip);

    2、解压后的文件夹包含有:library jars, source files, samples, API javadocs, and additional documentation;

    3、将jmockit.jar添加到项目classpath中;

    特别的:

    1)确保classpath中Jar包的顺序:jmockit的jar包必须在junit之前(通过Order and Export" 标签上下移动);

    2)eclipse项目所用JRE来自于JDK,而不是“简洁”版的JRE,因为后者缺少本地类库“attach”。

    二、行为和状态的测试

    基于行为(Behavior-based)的mock是站在目标测试代码外面的角度的,通常主要模拟行为,而基于状态

    (State-based)的是站在目标测试代码内部的。我们可以对传入的参数进行检查、匹配,才返回某些结果。

    Mockup用于State-based测试。

    二、声明和使用mock类型

    1、字段,期望块的字段与期望块内的局部属性字段使用@Mocked来声明Mock类型。

    2、参数,方法的参数声明来引入一个Mock类型。

    第一种情况,属性字段是属于测试类或者一个mockit.Expectations子类(一个expectation期望块的内部的局部

    属性字段)。

    第二种情况,参数必须是属于某个测试方法(@Test标签下的方法)。

    在所有的情况,一个mock属性字段或者参数声明,都可以通过使用@Mocked声明。对于方法mock的参数或者

    在expectations期望块中定义的mock属性字段来说,该注解是可选的,而对于定义在测试类(XXXTest类)中的

    属性字段,@Mocked标签是必须,这是为了防止和该测试类的其它不需要mock的字段属性产生冲突。

    package main;
     
     
     
    import static org.junit.Assert.*;
     
     
     
    import java.io.Serializable;
     
     
     
    import org.junit.Test;
     
     
     
    import mockit.Expectations;
     
    import mockit.Mocked;
     
    import mockit.NonStrictExpectations;
     
    import mockit.Verifications;
     
     
     
    /*
     
    * 一个用户接口(外部依赖)
     
    */
     
    interface Dependency {
     
    String doSomething(boolean b);
     
    }
     
     
     
    // 声明变量类型MultiMock(它实现两个接口,作用域是整个测试类)
     
    public class MultiMocksTest<MultiMock extends Dependency & Runnable> {
     
     
     
    @Mocked
     
    MultiMock multiMock;
     
     
     
    @Test
     
    public void mockFieldWithTwoInterfaces() {
     
    new NonStrictExpectations() {
     
    {
     
    multiMock.doSomething(false);
     
    result = "test";
     
    }
     
    };
     
     
     
    multiMock.run();
     
    assertEquals("test", multiMock.doSomething(false));
     
     
     
    // 验证run()方法执行一次
     
    new Verifications() {
     
    {
     
    multiMock.run();
     
    }
     
    };
     
    }
     
     
     
    @Test
     
    // 声明变量类型M,它实现两个接口,作用域为该测试方法
     
    // final M mock 前的@Mocked注解是可选的
     
    public <M extends Dependency & Serializable> void mockParameterWithTwoInterfaces(
     
    @Mocked final M mock) {
     
    new Expectations() {
     
    {
     
    mock.doSomething(true);
     
    result = "";
     
    }
     
    };
     
     
     
    assertEquals("", multiMock.doSomething(true));
     
    }
     
    }
     

    3、对于一个返回值不为void类型的方法,Expectations中如何模拟方法返回值:

    1)其返回值可以通过Expectations的result属性域来记录

    2)Expectations的returns(Object)方法来记录

    例如,方法返回一个Throwable异常类,只需将一个类型实验赋给result(注意,异常类只能通过result方式

    赋值)。

     
    package main;
     
     
     
    import mockit.Expectations;
     
     
     
    import org.junit.Test;
     
     
     
    public class UnitUnderTest {
     
     
     
    // 1、构造方法
     
    private OutWork work = new OutWork();
     
     
     
    public void doSomthing() {
     
    // 2、intReturningMethod()方法
     
    int n = work.intReturningMethod();
     
     
     
    for (int i = 0; i < n; i++) {
     
    String s;
     
    try {
     
    // 3、stringReturningMethod()方法
     
    s = work.stringReturningMethod();
     
    } catch (Exception e) {
     
    // 处理异常
     
    e.printStackTrace();
     
    }
     
    }
     
     
     
    // 其它逻辑...
     
    }
     
     
     
    @Test
     
    public void test() {
     
    new Expectations() {
     
    OutWork work;
     
    {
     
    // 1、构造方法模拟
     
    new OutWork();
     
     
     
    // 2、intReturningMethod()方法模拟
     
    work.intReturningMethod();
     
    result = 3;
     
     
     
    // 3、stringReturningMethod()方法模拟
     
    work.stringReturningMethod();
     
    // 方法分别返回三个值,两个字符串,一个异常
     
    returns("str1", "str2");
     
    result = new Exception("testException");
     
    }
     
    };
     
     
     
    new UnitUnderTest().doSomthing();
     
    }
     
    }
     
     
     
    class OutWork {
     
    public int intReturningMethod() {
     
    return 0;
     
    }
     
     
     
    public String stringReturningMethod() {
     
    return "";
     
    }
     
    }
     

    三、从严格到非严格

    1、@Mocked+Expections块:会进行隐式校验(执行顺序和次数)

    2、@Mocked+NonStrictExpections块:NonStrictExpections块中的Incovation可以非严格执行(不执行或者

    执行N次,除非显示地指定执行次数)。

     
    package main;
     
    /*
     
    * 用于Mock的接口
     
    */
     
    public interface WinportUrlService {
     
    public boolean hasWinport(String id);
     
    public String getMsg();
     
    public Throwable getWinportUrlThrowException(String id);
     
    }
     
    
     
    
     
    package main;
     
     
     
    import static org.junit.Assert.*;
     
    import mockit.Expectations;
     
    import mockit.Mocked;
     
    import mockit.NonStrictExpectations;
     
     
     
    import org.junit.Test;
     
     
     
    public class IntroductionTest {
     
     
     
    // @Mocked注解+Expectations/NonStrictExpectations块
     
    @Mocked
     
    private WinportUrlService winportUrlService = null;
     
     
     
    @Test
     
    public void testNoExceptions() {
     
    final String memberId = "test";
     
    // 未指定期望块,方法返回默认值
     
    assertEquals(false, winportUrlService.hasWinport(memberId));
     
    assertEquals(null, winportUrlService.getMsg());
     
    assertEquals(null, winportUrlService.getWinportUrlThrowException(memberId));
     
    }
     
     
     
    @Test
     
    public void testWithExpectations() {
     
    final String memerId = "test";
     
     
     
    // 步骤一:record
     
     
     
    // 严格期望块
     
    new Expectations() {
     
    {
     
    // 下面的Invocation必须严格执行
     
    winportUrlService.hasWinport(memerId);
     
    result = false;// 也可以是returns(false)
     
     
     
    // 未指定执行次数
     
    }
     
    };
     
     
     
    // 非严格期望块
     
    new NonStrictExpectations() {
     
    {
     
    // 下面的Invocation非严格执行
     
    winportUrlService.getMsg();
     
    result = "test";// 也可以是returns("test")
     
     
     
    // 未指定执行次数
     
    }
     
    };
     
     
     
    // 步骤二:replay阶段
     
     
     
    // hasWinport必须严格执行一次
     
    assertEquals(false, winportUrlService.hasWinport(memerId));
     
     
     
    // getMsg可以不执行或执行多次
     
    assertEquals("test", winportUrlService.getMsg());
     
    assertEquals("test", winportUrlService.getMsg());
     
     
     
    // 下面的Invocation失败
     
    // winportUrlService.getWinportUrlThrowException(memerId);
     
    }
     
    }

    3、@NonStrict:可以在replay中调用或不调用。@NonStrict可以避免需要记录调用构造函数,或任何不感兴

    趣的方法。

    注意:@NonStrict它是针对类的属性非严格,类的属性适用于类中的所有测试方法。这个非严格的范围比

    NonStrictExpections块的作用范围大很多,一旦使用了@NonStrict,Expections中的Incovation就变成了非严

    格的invocation,因此其它测试方法还想在该属性的基础上使用Expections块是不可能的了。如果是这种情况

    就需要这样使用(@Mocked+NonStrictExpectations块)。

    package main;
     
     
     
    import static org.junit.Assert.*;
     
    import mockit.Expectations;
     
    import mockit.NonStrict;
     
     
     
    import org.junit.Test;
     
     
     
    public class IntroductionTest2 {
     
     
     
    // @NonStrict注解
     
    @NonStrict
     
    private WinportUrlService winportUrlService = null;
     
     
     
    @Test
     
    public void testWithExpectations() {
     
    final String memberId = "test";
     
     
     
    // 步骤一:record
     
     
     
    // 用了@NonStrict,Expectation中的invocation就变成了非严格的invocation
     
    new Expectations() {
     
    {
     
    // 下面的Invocation非严格执行
     
    winportUrlService.hasWinport(memberId);
     
    result = false;// 也可以是returns(false)
     
     
     
    // 未指定执行次数
     
    }
     
    };
     
     
     
    // 步骤二:replay阶段
     
     
     
    // hasWinport可以不执行或执行多次
     
    assertEquals(false, winportUrlService.hasWinport(memberId));
     
    assertEquals(false, winportUrlService.hasWinport(memberId));
     
     
     
    // 下面的Invocation成功
     
    winportUrlService.getWinportUrlThrowException(memberId);
     
     
     
    }
     
    }
     

    4、此外,若不指定执行次数,Expections块默认必须执行一次,NonStrictExpections块中的Invocation可执行

    N次或不执行;若显式地指定执行次数(N次),二者的Invocation都必须执行N次

  • 相关阅读:
    MySQL之存储过程和函数
    Mysql中的常用函数:
    Mysql之使用Mysql运算符
    Mysql之单表记录查询
    Mysql之触发器的操作:
    Mysql之视图的操作
    Mysql之表的操作与索引操作
    Mysql之基本操作与数据类型
    Java多线程之生产者消费者
    Java多线程
  • 原文地址:https://www.cnblogs.com/wsy0202/p/11367385.html
Copyright © 2020-2023  润新知