PowerMock基本场景示范

PowerMockIto和MockIto是我们使用的Javer的单测框架。也是基于Junit扩展来的,其提供了更强大的一些功能,单测的重要性直接关系到代码质量,这是非常重要的,尤其在迭代快速的互联网公司,开发往往承担了不仅仅是“开发”的工作。熟练掌握测试框架是非常有意义的。


1. 首先是最基本的mock方法

依赖:

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-easymock</artifactId>
    <version>2.0.2</version>
    <scope>test</scope>
</dependency>

public class UserService {
    public String getUserName() {
        return "real user name";
    }
}
public void testUser() {
    UserService userService = Mockito.mock(UserService.class);
    Mockito.when(userService.getUserName()).thenReturn("mock name");
    System.out.println(userService.getUserName());
}

输出:
mock name

2. @mock 注解方式,类上加上@RunWith(MockitoJUnitRunner.class)
@RunWith(MockitoJUnitRunner.class) // 注意这里别忘记
public class UserMockTest {
    @Mock
    private UserService userService;

    @Test
    public void testUser() {
        Mockito.when(userService.getUserName()).thenReturn("mock name");
        System.out.println(userService.getUserName());
    }
}

输出:mock name

3. Spring依赖注入mock测试

在这里插入图片描述
主要就是UserService的部分逻辑我需要测试,但是其调用NameService的方法我需要去mock替换。此依赖是基于 Spring的容器管理。我们该怎样去mock呢。
这里要mock. UserService 的 getUserName方法,可能其中有一个注入调用this.nameService.getName()我们需要mock。
可以使用这样的:

public class UserService {
    @Resource
    private NameService nameService;

    public String getUserName() {
        /**
         * 可测试部分
         */
        return this.nameService.getName();
    }
}
@RunWith(MockitoJUnitRunner.class)
public class UserMockTest {
    @Spy // 真实的类
    @InjectMocks   // 内部依赖可注入
    private UserService userService = new UserService();

    @Mock
    public NameService nameService;

    @Before
    public void testBefore() {
        Mockito.when(nameService.getName()).thenReturn("mock name service");
    }

    @Test
    public void testUser() {
        System.out.println(userService.getUserName());
    }
}

输出 mock name service
点评:Spy注解就是说手动new一个类去替换,至于里面的对象是否注入mock的对象,用@InjectMocks去生效。

5. static 方法mock

到这里,就是一些复杂场景了,这里网上你去搜索会发现各种稀奇古怪的报错信息,例如字节码错误,或则是npe或则其他问题,但是最终都会发现跟PowerMockIto和MockIto和字节码甚至是JDK的版本兼容性问题,Github有人po出了对象版本的对象关系,仅供参考:
https://github.com/powermock/powermock/wiki/Mockito
我也是调试了很久,最终能成功跑起来的依赖如下:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>2.9.0</version>
</dependency>

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito2</artifactId>
    <version>1.7.3</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>1.7.3</version>
    <scope>test</scope>
</dependency>

如果报java.lang.LinkageError: loader constraint violation: loader (instance of org/powermock/core/classloader/MockClassLoader) previously initiated loading for a different type with name “javax/management/MBeanServer” 类似的错误,一般都是JDK或则其它三方库兼容性问题,可以采用 @PowerMockIgnore(“javax.management.*”) 解决

@RunWith(PowerMockRunner.class)
@PowerMockIgnore("javax.management.*") // 忽略加载包下面的类
@PrepareForTest({RequestContextHolder.class}) // 需要 mock 类静态方法,需要在此声明

static方法的mock如下:

public class UserService {

    public static String getStaticName() {
        return "real staticName";
    }
}

外面调用static方法希望mock。可以如下的方法

@RunWith(PowerMockRunner.class) // 此处指定 runner
@PrepareForTest(UserService.class) // 此处指定static方法的类,可以是数组
public class UserMockTest /*extends BaseTest*/ {

    @Before
    public void before() {
        PowerMockito.mockStatic(UserService.class);
        PowerMockito.when(UserService.getStaticName()).thenReturn("mock static user name");
    }

    @Test
    public void testUser() {
        System.out.println(UserService.getStaticName());
    }
}

输出:mock static user name

6. mock对象的继承 构建模块化集成测试基础
class BaseTest {
}

@RunWith(PowerMockRunner.class)
public class UserMockTest extends BaseTest{
    @Mock
    public UserService userService;

    @Before
    public void before() {
        PowerMockito.when(userService.getUserName()).thenReturn("sub mock name");
    }

    @Test
    public void testUser() {
        System.out.println(userService.getUserName());
    }
}

最开始没改任何依赖,直接报错,可以看到我只是添加了一个继承关系而已。
在这里插入图片描述
查找了很多资料:
在这里插入图片描述
意思就是javassist的版本校验问题,建议使用最新的版本javassist。
随后添加最新的javassist版本然后问题得到解决。:

<dependency>
    <groupId>javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.12.1.GA</version>
</dependency>

但是这里javassist在maven中央仓库会发现很多版本,看清楚groupId和artifcatId。

  • 这里有个问题,如果父类和子类都mock了,会以哪个值为准呢。如下
/**
 * 父类继承mock
 */
class BaseTest {

    @Mock
    public UserService userService;

    @Before
    public void before() {
        PowerMockito.when(userService.getUserName()).thenReturn("base mock name");
    }
}

@RunWith(PowerMockRunner.class)
public class UserMockTest extends BaseTest{

    @Before
    public void before() {
        PowerMockito.when(userService.getUserName()).thenReturn("sub mock name");
    }

    @Test
    public void testUser() {
        System.out.println(userService.getUserName());
    }
}

输出:sub mock name
debug发现,因为子类before重写了,所以父亲before没执行而已。

  • 如果子类与父类重复mock:
@RunWith(PowerMockRunner.class)
public class UserMockTest extends BaseTest{
    /**
     * 先执行
     */
    @Before
    public void before2() {
        PowerMockito.when(userService.getUserName()).thenReturn("sub mock name");
    }

    @Test
    public void testUser() {
        System.out.println(userService.getUserName());
    }
}


/**
 * 父类继承mock
 */
class BaseTest {

    @Mock
    public UserService userService;

    /**
     * 后执行
     */
    @Before
    public void before() {
        PowerMockito.when(userService.getUserName()).thenReturn("base mock name");
    }
}

输出:base mock name
说明@Before注解是先执行子类的方法,再执行父类的方法。

  • 如果子类注入相同类并重复mock会出现怎样的情况
@RunWith(PowerMockRunner.class)
public class UserMockTest extends BaseTest{

    // 子类重新定义了userService
    @Mock
    public UserService userService;
    /**
     * 先执行
     */
    @Before
    public void before2() {
        /**
         * 子类重复mock
         */
        PowerMockito.when(userService.getUserName()).thenReturn("sub mock name");
    }

    @Test
    public void testUser() {
        System.out.println(userService.getUserName());
    }
}


/**
 * 父类继承mock
 */
class BaseTest {

    @Mock
    public UserService userService;

    /**
     * 后执行
     */
    @Before
    public void before() {
        PowerMockito.when(userService.getUserName()).thenReturn("base mock name");
    }
}

输出:sub mock name.
Debug看对象实例,发现子类与父类的userService并不是一个对象。所以子类的调用并不是父类的对象。UserMockTest
里mock的userService也是mock的子类引用实例的对象。如果改成

@Before
public void before2() {
    /**
     * 子类重复mock
     */
    PowerMockito.when(super.userService.getUserName()).thenReturn("sub mock name");
}

则输出 null.
因为testUser里调用的是子类的对象,该对象并没被mock方法。

7. Private方法mock
public class UserService {
	public String callPrivateMethodGetName() {
        return getPrivateMethodName();
    }
    private String getPrivateMethodName() {
        return "real private name";
    }
}

测试

@RunWith(PowerMockRunner.class)
@PrepareForTest(UserService.class)// 注意这里要指定private方法的类
public class UserMockTest {

    @Spy
    public UserService userService = new UserService();

    @Before
    public void before2() {
        try {
            PowerMockito.doReturn("mock private name").when(userService, "getPrivateMethodName");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testUser() {
        System.out.println(userService.callPrivateMethodGetName());
    }
}

输出:mock private name

8. Mock范性返回
class UserService {

    public <T> T getUser() {
        return null;
    }
}

interface User {
    String getIdentify();
}

class Manager implements User {
    @Override
    public String getIdentify() {
        return "real manager";
    }
}
class Employee implements User {
    @Override
    public String getIdentify() {
        return "real employee";
    }
}

@RunWith(PowerMockRunner.class)
public class UserMockTest {

    @Mock
    public UserService userService;

    @Before
    public void before2() {
        try {
            PowerMockito.doReturn((User) () -> "mock user").when(userService).getUser();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testUser() {
        User user = userService.getUser();
        System.out.println(user.getIdentify());
    }
}

输出:mock user
点评:关键在于doReturn的方式, when的对象的方法在when的返回值OngoingStubbing里。

补充:如果是final类mock的话也需要添加到PrepareForTest类标里面,否则会报初始化错误。


以上就是PowerMock的mock示范,这些场景基本能够覆盖绝大部分的case,个人感觉PowerMock的兼容性做的很差并且依赖的二方库也比较多,虽然有easymock这样的快速继承框架,但是感觉也是个beta版。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值