EasyMock详解

本文介绍了EasyMock库在Java单元测试中的使用,包括如何创建Mock对象、使用注解、录制和验证行为,以及各种参数匹配器的用法。
摘要由CSDN通过智能技术生成

EasyMock

1、概述

EasyMock 是一套用于通过简单的方法对于给定的接口生成 Mock 对象的类库。EasyMock提供对接口的模拟,能够通过录制、回放、检查三步来完成大体的测试过程,可以验证方法的调用种类、次数、顺序,可以令 Mock 对象返回指定的值或抛出指定异常。

Maven依赖:

<dependency>
    <groupId>org.easymock</groupId>
    <artifactId>easymock</artifactId>
    <version>4.3</version>
    <scope>test</scope>
</dependency>

使用 EasyMock包括以下几个步骤:

  1. 创建目标类的Mock
  2. 录制(Recording)其预期行为,包括动作、结果、调用次数、异常等
  3. 录制完成后,我们将其切换到重播(Replay)模式,以便Mock在被使用者调用时,表现得和录制时一样
  4. 在测试中使用mock对象
  5. 验证它是否按预期运行

2、简单示例

被Mock的接口:

public interface CalculatorService {
    
    double add(double a, double b);
    
    double subtract(double a, double b);
    
    double multiply(double a, double b);
    
    double divide(double a, double b);
}

应用,依赖于CalculatorService接口:

public class MathApplication {

    private CalculatorService calculatorService;

    public MathApplication(CalculatorService calculatorService) {
        this.calculatorService = calculatorService;
    }

    public double add(double a, double b) {
        return calculatorService.add(a, b);
    }

    public double subtract(double a, double b) {
        return calculatorService.subtract(a, b);
    }

    public double multiply(double a, double b) {
        return calculatorService.multiply(a, b);
    }

    public double divie(double a, double b) {
        return calculatorService.divide(a, b);
    }
}

测试:

//指定EasyMock运行器
@RunWith(EasyMockRunner.class)
public class MathApplicationTest {

    @Mock//指示哪个对象会被Mock创建
    private CalculatorService calculatorService;

    @TestSubject//指示哪个类会使用Mock对象
    private MathApplication mathApplication = new MathApplication(calculatorService);

    @Test
    public void add() {
        //recording,录制mock对象的预期行为,包括动作、结果、异常等
        EasyMock.expect(calculatorService.add(10.0, 20.0))
                .andReturn(30.0);
        EasyMock.expect(calculatorService.add(11.0, 20.0))
                .andReturn(31.0);

        //Replay,录制完成后,切换到重播模式,以便Mock在被使用者调用时,表现得和录制时一样
        EasyMock.replay(calculatorService);

        //在测试中使用
        Assert.assertEquals(mathApplication.add(10.0, 20.0),30.0,0);
        Assert.assertEquals(mathApplication.add(11.0, 20.0),31.0,0);

        //验证mock对象是否按预期运行
        EasyMock.verify(calculatorService);
    }

}

3、使用注解Mock对象

EasyMock注释可用于创建Mock对象。 我们还可以告诉EasyMock框架将这些模拟对象注入到另一个具体的类中。

3.1、常用注解

  • @Mock:用于指定EasyMock要模拟的字段。
  • @TestSubject:用于指定一个对象,我们希望EasyMock在其中注入使用@Mock注释创建的对象。

3.2、使用方式

当我们使用EasyMock注释时,我们必须通过以下方法之一显式初始化它们:

  • @RunWith(EasyMockRunner.class):如果我们使用的是JUnit 4,可以使用它,请注意, JUnit 5仍然不支持此功能。 如果我们将其与JUnit 5一起使用,则它将回退以使用JUnit 4运行器类。
  • org.easymock.EasyMockRule:该方式利用了JUnit 4规则,因此同样不能与JUnit 5一起使用。
  • EasyMockSupport.injectMocks(this):可以在@Before方法中使用它来告诉EasyMock注入模拟对象。 这是JUnit 5和TestNG框架的首选方法。

被Mock的接口:

public interface IntegerUtils {

    int add(int x, int y);
}


public interface StringUtils {

    String reverse(String input);

    String convert(int i);
}

应用类,依赖接口:

public class MyUtils {

    private StringUtils su;
    private IntegerUtils iu;

    public MyUtils(StringUtils su, IntegerUtils iu) {
        this.su = su;
        this.iu = iu;
    }

    public int add(int i, int j) {
        return iu.add(i, j);
    }

    public String reverse(String s) {
        return su.reverse(s);
    }

    public String convert(int i) {
        return su.convert(i);
    }
}
1)、使用@RunWith
@RunWith(EasyMockRunner.class)
public class EasyMockAnnotationsRunWithExample {

    @Mock
    StringUtils mockSU;

    @Mock
    IntegerUtils mockIU;

    @TestSubject
    MyUtils mu = new MyUtils(mockSU, mockIU);

    @Test
    public void test() {
        EasyMock.expect(mockIU.add(10, 20)).andReturn(30);
        EasyMock.expect(mockSU.convert(10)).andReturn("10");
        EasyMock.expect(mockSU.reverse("hello")).andReturn("olleh");

        EasyMock.replay(mockSU, mockIU);

        Assert.assertEquals(30, mu.add(10, 20));
        Assert.assertEquals("10", mu.convert(10));
        Assert.assertEquals("olleh", mu.reverse("hello"));
    }
}
2)、使用EasyMockRule
public class EasyMockAnnotationsEasyMockRuleExample {

    @Mock
    StringUtils mockSU;

    @Mock
    IntegerUtils mockIU;

    @TestSubject
    MyUtils mu = new MyUtils(mockSU, mockIU);

    @Rule
    public EasyMockRule easyMockRule = new EasyMockRule(this);

    @Test
    public void test() {
        EasyMock.expect(mockIU.add(10, 20)).andReturn(30);
        EasyMock.expect(mockSU.convert(10)).andReturn("10");
        EasyMock.expect(mockSU.reverse("hello")).andReturn("olleh");

        EasyMock.replay(mockSU, mockIU);

        Assert.assertEquals(30, mu.add(10, 20));
        Assert.assertEquals("10", mu.convert(10));
        Assert.assertEquals("olleh", mu.reverse("hello"));
    }

}
3)、使用EasyMockSupport
public class EasyMockAnnotationsInjectExample {

    @Mock
    StringUtils mockSU;

    @Mock
    IntegerUtils mockIU;

    @TestSubject
    MyUtils mu = new MyUtils(mockSU, mockIU);

    @Before
    public void setup() {
        EasyMockSupport.injectMocks(this);
    }

    @Test
    public void test() {
        EasyMock.expect(mockIU.add(10, 20)).andReturn(30);
        EasyMock.expect(mockSU.convert(10)).andReturn("10");
        EasyMock.expect(mockSU.reverse("hello")).andReturn("olleh");

        EasyMock.replay(mockSU, mockIU);

        Assert.assertEquals(30, mu.add(10, 20));
        Assert.assertEquals("10", mu.convert(10));
        Assert.assertEquals("olleh", mu.reverse("hello"));
    }
}

4、EasyMock常用方法

4.1、创建mock对象

  • EasyMock.createMock
CalculatorService calculatorService = EasyMock.createMock(CalculatorService.class);
  • EasyMock.mock
ArrayList<Object> mockList = EasyMock.mock(ArrayList.class);
  • EasyMock.createStrictMock:只允许此Mock按expect定义的顺序被方法调用,也就是说,Mock会检查方法调用的顺序。任何expect之外的方法调用都会导致AssertionError
CalculatorService calculatorService = EasyMock.createStrictMock(CalculatorService.class);
  • EasyMock.createNiceMock:创建了模拟,并设置模拟的每个方法的默认实现。如果使用EasyMock.createMock(),然后模拟方法调用将抛出断言错误
CalculatorService calculatorService = EasyMock.createNiceMock(CalculatorService.class);

4.2、录制mock对象行为

  • EasyMock.expect
  • EasyMock.expectLastCall
CalculatorService calculatorService = EasyMock.createMock(CalculatorService.class);
//录制mock对象行为,方法有返回值
EasyMock.expect(calculatorService.add(1.0, 2.0))
        .andReturn(3.0)//指定返回值
        .times(3)//指定调用次数
        .andThrow(new NullPointerException());

//录制mock对象行为,方法没有返回值
calculatorService.someVoidMethod(1.0, 2.0);
EasyMock.expectLastCall()
        .times(3)//指定调用次数
        .andThrow(new NullPointerException());

4.3、进入replay阶段

  • EasyMock.replay
EasyMock.replay(calculatorService);

4.4、验证verfiy

  • EasyMock.verify
EasyMock.verify(calculatorService);

4.5、调用次数

EasyMock提供很多的方法来改变预期的调用计数:

  • times(int min, int max):最小值和最大值之间的预期调用
  • atLeastOnce():预期至少有一个调用
  • anyTimes():预期调用的数量不受限制

5、参数匹配器

EasyMock参数匹配器使我们可以在对方法进行存根时提供灵活的参数以进行匹配。 您将在EasyMock中找到许多any*()方法,这些方法可以与expect()一起使用,以提供方法调用的参数。

5.1、一般参数匹配器

可以使用anyXxx()方法来将参数指定为任何对象。

  • anyBoolean():任意Boolean
  • anyByte():任意byte
  • anyChar():任意char
  • anyInt():任意int
  • anyLong():任意long
  • anyFloat():任意float
  • anyDouble():任意double
  • anyShort():任意short
  • anyObject():任意Object
  • anyObject(Class<T>):任意指定类型的Object
  • anyString():任意String
import org.junit.Test;


import java.util.ArrayList;
import java.util.HashSet;

import static org.easymock.EasyMock.*;
import static org.junit.Assert.*;

public class EasyMockAgrumentMatcherExample {

    @Test
    public void test() {
        HashSet<Object> set = mock(HashSet.class);

        expect(set.add(anyBoolean())).andReturn(true);
        expect(set.add(anyByte())).andReturn(true);
        expect(set.add(anyChar())).andReturn(true);
        expect(set.add(anyInt())).andReturn(false);
        expect(set.add(anyLong())).andReturn(false);
        expect(set.add(anyFloat())).andReturn(false);
        expect(set.add(anyDouble())).andReturn(false);
        expect(set.add(anyShort())).andReturn(false);
        expect(set.add(anyObject(ArrayList.class))).andReturn(false);
        expect(set.add(anyString())).andReturn(true);
        expect(set.add(anyObject())).andReturn(true);

        replay(set);

        assertTrue(set.add(true));
        assertTrue(set.add((byte)1));
        assertTrue(set.add('A'));
        assertFalse(set.add(Integer.valueOf(1)));
        assertFalse(set.add(Long.valueOf(1)));
        assertFalse(set.add(Float.valueOf(2.1f)));
        assertFalse(set.add(Double.valueOf(2.22)));
        assertFalse(set.add(Short.valueOf("1")));
        assertFalse(set.add(new ArrayList<>()));
        assertTrue(set.add("hi"));
        assertTrue(set.add(new Object()));
    }
}

5.2、等于参数匹配器

可以使用same()参数匹配器方法对同一个对象的行为进行存根。 如果要执行相等性检查,请使用eq()方法。

  • same():相当于比较引用
  • eq():相当于equals
  • aryEq():比较数组内容
  • isA():比较类型
import org.junit.Test;

import static org.easymock.EasyMock.*;
import static org.junit.Assert.*;


public class EasyMockAgrumentMatcherEqualityExample {

    @Test
    public void test() {
        Utils mock = mock(Utils.class);
        Object obj = new Object();

        //相当于比较引用
        expect(mock.convert(same(obj))).andReturn("True");
        //相当于equals
        expect(mock.convert(eq("ABC"))).andReturn("Awesome");

        expect(mock.convert(anyObject())).andReturn("False");
        replay(mock);

        assertEquals("True", mock.convert(obj));
        assertEquals("Awesome", mock.convert("ABC"));

        assertEquals("False", mock.convert(new Object()));
    }
}

class Utils {
    public String convert(Object obj) {
        return obj.toString();
    }
}

5.3、比较参数匹配器

我们可以使用lt() , gt() , leq()和geq()方法来比较参数并返回特定的输出。 请注意,这些方法被重载以与原始数据类型以及对象一起使用。 如果将它们与任何对象一起使用,则它们应该是Comparable 。

  • lt():小于
  • gt():大于
  • leq():小于等于
  • geq():大于等于
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

import static org.easymock.EasyMock.*;
import static org.junit.Assert.*;

public class EasyMockAgrumentMatcherComparisonExample {

    @Test
    public void test() {
        List mock = mock(ArrayList.class);

        expect(mock.add(lt(10))).andReturn(true);
        expect(mock.add(geq(10))).andReturn(false);

        replay(mock);

        assertTrue(mock.add(5));
        assertFalse(mock.add(100));
    }
}

5.4、条件参数匹配器

我们可以将and() , or()和not()函数与参数匹配器一起使用,以编写更复杂的行为。

  • and():逻辑与
  • or():逻辑或
  • not():逻辑非

注意,当我们使用参数匹配器对行为进行存根时,每个参数都必须是匹配器,否则,我们将收到一条错误消息。 因此,如果我们必须指定not(100) ,则将其写为not(eq(100)) 。

import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

import static org.easymock.EasyMock.*;
import static org.junit.Assert.*;


public class EasyMockAgrumentMatcherConditionalExample {

    @Test
    public void test() {
        List<Integer> mock = mock(ArrayList.class);

        //大于0且小于10
        expect(mock.add(and(gt(0), lt(10)))).andReturn(true);
        //等于33或等于37
        expect(mock.add(or(eq(33), eq(77)))).andReturn(true);
        //不是100
        expect(mock.add(not(lt(100)))).andReturn(false);

        replay(mock);

        assertTrue(mock.add(5));
        assertTrue(mock.add(33));
    }
}

5.5、空参数匹配器

我们可以使用isNull()和notNull()来匹配null和notNull()参数。 但是,当我们使用其他参数匹配器时, isNull()会派上用场,并且不能直接使用null。

  • isNull():为Null
  • notNull():不为Null
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

import static org.easymock.EasyMock.*;
import static org.junit.Assert.*;

public class EasyMockAgrumentMatcherNullExample {

    @Test
    public void test() {
        List<Object> mock = mock(ArrayList.class);

        expect(mock.add(isNull())).andReturn(true);
        expect(mock.add(notNull())).andReturn(false);
        replay(mock);

        assertTrue(mock.add(null));
        assertFalse(mock.add("Hi"));
    }
}

5.6、字符串参数匹配器

  • contains(String):包含子串
  • find(String):子字符串之一与正则表达式匹配
  • matches(String):数字符串与正则表达式匹配
  • startsWith(String):以…开头
  • endsWith(String):以…结尾
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

import static org.easymock.EasyMock.*;
import static org.junit.Assert.*;

public class EasyMockAgrumentMatcherStringExample {

    @Test
    public void test() {
        List<String> mock = mock(ArrayList.class);

        //以Java开头
        expect(mock.add(startsWith("Java"))).andReturn(true);
        //以Dev结尾
        expect(mock.add(endsWith("Dev"))).andReturn(true);
        //包含Pyt
        expect(mock.add(contains("Pyt"))).andReturn(true);
        //正则匹配
        expect(mock.add(matches("^[abc]d."))).andReturn(true);
        expect(mock.add(find("[9]{3}"))).andReturn(true);

        replay(mock);

        assertTrue(mock.add("Java World"));
        assertTrue(mock.add("JournalDev"));
        assertTrue(mock.add("Python"));
        assertTrue(mock.add("ads"));
        assertTrue(mock.add("ABC999DDD"));

        verify(mock);
    }
}
  • 23
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
为了使用 EasyMock 发出 POST 请求,你可以按照以下步骤进行操作: 1. 首先,你需要创建一个模拟的 HTTP 请求对象。在 EasyMock 中,你可以使用 `org.easymock.EasyMock` 类的 `createMock` 方法创建该对象。 ```java import org.easymock.EasyMock; // 创建模拟的 HTTP 请求对象 HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class); ``` 2. 接下来,你需要设置该请求对象的相关属性,例如请求方法、请求路径、请求参数等。 ```java // 设置请求方法为 POST EasyMock.expect(request.getMethod()).andReturn("POST").anyTimes(); // 设置请求路径 EasyMock.expect(request.getRequestURI()).andReturn("/your-api-endpoint").anyTimes(); // 设置请求参数 EasyMock.expect(request.getParameter("paramName")).andReturn("paramValue").anyTimes(); ``` 3. 然后,你需要创建一个模拟的 HTTP 响应对象,并设置相应的属性。 ```java // 创建模拟的 HTTP 响应对象 HttpServletResponse response = EasyMock.createMock(HttpServletResponse.class); // 设置响应状态码 response.setStatus(HttpServletResponse.SC_OK); // 设置响应内容类型 response.setContentType("application/json"); // 设置响应内容 response.getWriter().write("Response body"); ``` 4. 最后,你可以使用 `org.easymock.EasyMock` 类的 `replay` 和 `verify` 方法来启动和验证模拟对象。 ```java // 启动模拟对象 EasyMock.replay(request, response); // 执行你的代码逻辑,处理 POST 请求 // 验证模拟对象的方法是否按预期被调用 EasyMock.verify(request, response); ``` 通过以上步骤,你就可以使用 EasyMock 模拟一个 POST 请求,并进行相应的测试。请根据你的具体需求,适当修改代码中的请求路径、请求参数、响应状态码和响应内容等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值