Easymock和Powermock

Easymock

Mock对象用于在单元测试中模拟一些难以构造或者比较复杂的对象,从而把测试环境和外界环境隔开,easymock默认只支持模拟接口对象

1. 引入Easymock的jar包

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

2. 要模拟的接口

public interface Calculator {
    public int add(int a,int b);
    public int subtract(int a, int b);
    public int multiply(int a, int b);
    public int divide(int a, int b);
}

3. 被测试的类,调用了未实现的接口

public class ClassToEasyMock {
    private Calculator calculator;

    public int methodToTest(){
        int a = calculator.add(2,2);
        int b = calculator.divide(2,2);
        int c = calculator.multiply(2,2);
        int d = calculator.subtract(2,2);
        return a+b+c+d;
    }
    public Calculator getMock() {
        return calculator;
    }

    public void setMock(Calculator calculator) {
        this.calculator = calculator;
    }
}

4. 测试类

public class ClassToEasyMockTest {
    @Test
    public void testEasyMock() {
        //1.创建mock对象
        Calculator mock = EasyMock.createMock(Calculator.class);
        //2.设置预期的函数行为
        EasyMock.expect(mock.add(2,2)).andReturn(4);
        EasyMock.expect(mock.divide(2,2)).andReturn(1);
        EasyMock.expect(mock.multiply(2,2)).andReturn(4);
        EasyMock.expect(mock.subtract(2,2)).andReturn(0);
        //3.重放
        EasyMock.replay(mock);
        //实例对象验证
        ClassToEasyMock classToEasyMock = new ClassToEasyMock();
        classToEasyMock.setMock(mock);
        assertEquals(9,classToEasyMock.methodToTest());
        //4.验证mock对象
        EasyMock.verify(mock);
    }
}

Easymock的使用步骤

1. 创建mock对象
Calculator mock = EasyMock.createMock(Calculator.class);
如果有多个mock对象,可以使用EasyMock.createControl()方法
IMocksControl control = EasyMock.createControl();
control.createMock(A.class);
control.createMock(B.class);

2. 录制(设置函数的预期返回值)
(1) 有返回值
EasyMock.expect(mock.add(2,2)).andReturn(4);
(2) 没有返回值
EasyMock.expectLastCall(mock.mthod(arguments));
(3) 如果期望抛出异常
EasyMock.expect(mock.divide(3,0)).andThrow(new ArithmeticException());
(4) 预期调用方法的次数
EasyMock.expect(mock.add(2,2)).andReturn(4).once();
EasyMock.expect(mock.add(2,2)).andReturn(4).times(3);
EasyMock.expect(mock.add(2,2)).andReturn(4).anyTimes();

3. 重放
EasyMock.replay(mock);
control.repaly();

4. 验证
EasyMock.verify(mock);
control.verify();

PowerMock

PowerMock是对EasyMock的扩展,PowerMock通过自定义类加载器和修改字节码来模拟private函数,static函数,final函数以及构造函数,

1. 引入PowerMock的jar包

<properties>
        <powermock.version>1.6.4</powermock.version>
    </properties>
<dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-module-junit4</artifactId>
            <version>${powermock.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-api-mockito</artifactId>
            <version>${powermock.version}</version>
            <scope>test</scope>
        </dependency>

2. PowerMock注解
(1)@RunWith(PowerMockRunner.class)
指定PowerMock运行器,注意导入的是:org.powermock.modules.junit4.PowerMockRunner
而不是:org.powermock.modules.junit4.legacy.PowerMockRunner

(2)@PrepareForTest({ClassToMock.class})
指定要运行的类,使用PowerMock模拟私有,静态,final和构造方法时都需要修改字节码,需要将方法所在的类加入到PrepareForTest()里去。

(3)@PowerMockIgnore(“javax.management.*”)
解决不加时出现的classloader错误

(4)@Mock
创建mock对象,对象的所有方法为空,即不是真实的方法
普通方法:ClassToMock mock = Powermockito.mock(ClassToMock.class)

(5)@Spy
Spy注解的mock对象必须使用new方法手动创建,具有真实的方法和返回值
普通方法:ClassToMock mock= PowerMockito.spy(new ClassToMock());

(6)@InjectMocks
一般用于创建被测试类的实例,使用@Spy和@Mock创建的对象会注入到该实例中去。

@Mock注解和@Spy注解的区别:
(1)使用@Mock注解创建的对象所有的方法为空,默认的返回值为null
(2)使用@Spy注解创建的对象是真实的方法和真实的返回值,所以在设置函数的预期返回值时,如以下错误格式时,会先调用一次真实的方法,不符合我们的预期。
如果是@Mock对象,因为没有真实的方法,即使先执行一次也没有什么影响。

错误:PowerMockito.when(testclass,"privateMethod",Mockito.anyString()).thenReturn("SomeString");
正确:PowerMockito.doReturn("SomeString").when(testclass,"privateMethod",Mockito.anyString());

以下用powermock模拟私有函数来具体讲解@Mock和@Spy的区别。

3. PowerMock模拟私有函数
被测试类

public class PrivateMockClass {
    public String method1(String param){
        System.out.println(param);
      return privateMethod(param);
    }
    private String privateMethod(String param){
        String result = "Hi"+param;
        System.out.println(result);
        return result;
    }}

测试类

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import static org.junit.Assert.*;
@RunWith(PowerMockRunner.class)
@PrepareForTest({PrivateMockClass.class})
public class PrivateMockClassTest {
    @Spy//使用@spy注解需要手动new对象
    private PrivateMockClass testclass = new PrivateMockClass();
    @Mock
    private PrivateMockClass test;

    //使用@spy注解进行测试
    @Test
    public void testMethod1() throws Exception{

        //错误:PowerMockito.when(testclass,"privateMethod","private method").thenReturn("Hi private method");
        PowerMockito.doReturn("Hi private method").when(testclass,"privateMethod",Mockito.anyString());
        Assert.assertEquals("Hi private method",testclass.method1("123"));
    }

    //使用@Mock注解进行测试
    @Test
    public  void testMethod()throws Exception {
        PowerMockito.when(test,"privateMethod", Mockito.anyString()).thenReturn("Hi private method");
        //需要调用一次真实的方法,否则test.method1()返回null
        PowerMockito.when(test.method1(Mockito.anyString())).thenCallRealMethod();
        Assert.assertEquals("Hi private method",test.method1("456"));
    }
}

4. PowerMock模拟静态函数
被测试类

public class StaticMockClass {
    public int method(int a){
        return Ulity.staticMethod(a);
    }
}

静态函数

public class Ulity {
    public static int staticMethod(int a){
        return a+a;
    }
}

测试类

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.junit.Assert.*;

@RunWith(PowerMockRunner.class)
@PrepareForTest({Ulity.class})
public class StaticMockClassTest {

    @Spy
    private StaticMockClass testclass = new StaticMockClass();

    @Test
    public void method()throws Exception {
        PowerMockito.mockStatic(Ulity.class);
        PowerMockito.when(Ulity.staticMethod(Mockito.anyInt())).thenReturn(2);
        assertEquals(2,testclass.method(111));
    }
}

5. PowerMock模拟构造函数
被测试类,其中UserBean为一个实体类

public class ConstructorMockClass {
    public UserBean  method(){
        UserBean user = new UserBean(1,"张三","男");
        System.out.println("ID:"+user.getId()+"姓名:"+user.getName()+"性别:"+user.getSex());
        return user;
    }
}

测试类

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import static org.junit.Assert.*;
@RunWith(PowerMockRunner.class)
@PrepareForTest({ConstructorMockClass.class})
public class ConstructorMockClassTest {

    @Mock
    private UserBean user ;
    @InjectMocks
    private ConstructorMockClass testclass = new ConstructorMockClass();
    @Test
    public void method() throws Exception{
        //UserBean user = PowerMockito.mock(UserBean.class);
        PowerMockito.whenNew(UserBean.class).withArguments(Mockito.anyInt(),Mockito.anyString(),Mockito.anyString()).thenReturn(user);
        assertEquals(user,testclass.method());
    }
}

6. PowerMock模拟final函数
与powermock模拟私有函数类似

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值