当前位置: 代码迷 >> 综合 >> JUnit->Mockito->PowerMock->持续更新
  详细解决方案

JUnit->Mockito->PowerMock->持续更新

热度:48   发布时间:2023-12-12 07:39:53.0

最近在公司做需求,要求开发需要有相应的单元测试代码,第一次做单测相关的知识,就在这做一篇总结

一、JUnit

JUnit是Java最基础的测试框架,主要的作用就是断言。

方法名 方法描述
assertEquals 断言传入的预期值与实际值是相等的
assertNotEquals 断言传入的预期值与实际值是不相等的
assertArrayEquals 断言传入的预期数组与实际数组是相等的
assertNotNull 断言传入的对象是不为空
assertFalse 断言条件为假
assertSame 断言两个对象引用同一个对象,相当于“==”
assertThat 断言实际值是否满足指定的条件

常用注解

注解名 含义
@Test 表示此方法为测试方法
@Before 在每个测试方法前执行,可做初始化操作
@After 在每个测试方法后执行,可做释放资源操作
@BeforeClass 在类中所有方法前运行。此注解修饰的方法必须是static void
@Parameters 指定测试类的测试数据集合
@FixMethodOrder 指定测试类中方法的执行顺序
@RunWith 指定该测试类使用某个运行器

JUnit常用方法

我们测试下面这个简单的时间转换工具类,来说明一下具体的用法。

class DateUtil {
    companion object {
    // 英文全称 如:2017-11-01 22:11:00val FORMAT_YMDHMS = "yyyy-MM-dd HH:mm:ss"/*** 掉此方法输入所要转换的时间输入例如("2017-11-01 22:11:00")返回时间戳** @param time* @return 时间戳*/@Throws(ParseException::class)fun dateToStamp(time: String): Long {
    val sdr = SimpleDateFormat(FORMAT_YMDHMS, Locale.CHINA)val date = sdr.parse(time)return date.time}/*** 将时间戳转换为时间*/fun stampToDate(lt: Long): String {
    val simpleDateFormat = SimpleDateFormat(FORMAT_YMDHMS, Locale.CHINA)val date = Date(lt)return simpleDateFormat.format(date)}}
}

在这里插入图片描述
我们发现预期值和实际结果不符合,测试失败
接下来测试dateToStamp方法,这里写两个断言方法在这里插入图片描述
左下角这个绿色的勾勾就表示运行成功啦


测试异常

比如上述的dateToStamp()方法中,可能会抛出ParseException异常,我们可以验证是否抛出
在这里插入图片描述
验证一个方法是否抛出了异常,可以给@Test注解设置expected参数来实现
在这里插入图片描述


参数化测试

在这里插入图片描述
连续执行三次都成功(这里需要注意,每一次执行都是独立的,相互不影响)


二、Mockito

在实际的单元测试中,我们测试的类之间会有或多或少的耦合,导致我们无法顺利的进行测试,这时我们就可以使用Mockito,Mockito库能够Mock(我喜欢理解为模拟)对象,替换我们原先依赖的真实对象,这样我们就可以避免外部的影响,只测试本类,得到更准确的结果。

常用的Mock方式

class MockitoTest {
    /*** 方法1*/@Testfun testIsNotNull() {
    val person1 = mock(Person::class.java)Assert.assertNotNull(person1)}/*** 方法2 注解方法*/@Mocklateinit var person2: Person@Beforefun setUp() {
    MockitoAnnotations.initMocks(this)}@Testfun testIsNotNull2() {
    Assert.assertNotNull(person2)}/*** 方法3 MockitoRule*/@Mocklateinit var person3: Person@Rulepublic var mockitoRule = MockitoJUnit.rule()@Testfun testIsNotNull3() {
    Assert.assertNotNull(person3)}}

常用打桩方法

因为Mock出的对象中非void方法都将返回默认值,比如int方法将返回0,对象方法将返回null等,而void方法将什么都不做。“打桩”顾名思义就是将我们Mock出的对象进行操作,比如提供模拟的返回值等,给Mock打基础。
在这里插入图片描述

方法名 方法描述
thenReturn(T value) 设置要返回的值
thenThrow(Throwable… throwables) 设置要抛出的异常
thenAnswer(Answer<?> answer) 对结果进行拦截
doReturn(Object toBeReturned) 提前设置要返回的值
doCallRealMethod() 调用某一个方法的真实实现
doNothing() 设置void方法什么也不做

举例

open class Person constructor(name: String, sex: String) {
    val mName: Stringval mSex: Stringinit {
    mName = name;mSex = sex}open fun getName(): String {
    return mName}open fun getSex(): String {
    return mSex}open fun eat(something: String): String {
    return "吃什么吐什么"}
}

在这里插入图片的
成功返回小明并且抛出异常


thenAnswer——对方法进行拦截
在这里插入图片描述

行为测试

前面所说的都是状态测试,但是如果不关心返回结果,而是关心方法有否被正确的参数调用过,这时候就应该使用验证方法了。从概念上讲,就是和状态测试所不同的“行为测试”了
verify(T mock)验证发生的某些行为

方法名 方法描述
after(long millis) 在给定的时间后进行验证
timeout(long millis) 验证方法执行是否超时
atLeast(int minNumberOfInvocations) 至少进行n次验证
atMost(int maxNumberOfInvocations) 至多进行n次验证
times(int wantedNumberOfInvocations) 验证调用方法的次数
only() 验证方法只被调用一次,相当于times(1)
	@Testfun testPersonVerifyAfter() {
    Mockito.`when`(person2.getName()).thenReturn("jack")// 延时一秒验证println(person2.getName())println(System.currentTimeMillis())verify(person2, after(1000)).getName()println(System.currentTimeMillis())}@Testfun testPersonVerifyAtLeast() {
    person2.getSex()person2.getSex()// 至少验证2次verify(person2, atLeast(2)).getSex()// 该方法验证了两次verify(person2, times(2)).getSex()}// 常用参数匹配器@Testfun testPersonAny() {
    `when`(person2.eat(ArgumentMatchers.anyString())).thenReturn("一日三餐很固定")println(person2.eat("很固定?"))`when`(person2.eat(ArgumentMatchers.contains("面"))).thenReturn("面食")println(person2.eat("刀削面"))}

验证执行顺序在这里插入图片描述
将最后的
在这里插入图片描述


spy
在这里插入图片描述
Mockito框架不支持mock匿名类、final类、static方法、private方法。而PowerMock框架解决了这些问题。


三、PowerMock

package com.example.testdateunitabstract class Fruit {
    private val fruit = "水果"open fun getFruit(): String {
    return fruit}
}open class Banana: Fruit() {
    companion object {
    private val COLOR = "黄色的"@JvmStaticfun getColor(): String {
    return COLOR}}private fun flavor(): String {
    return "甜甜的"}fun getBananaInfo(): String {
    return flavor() + getColor()}fun isLike(): Boolean {
    return true}}
@PrepareForTest(Banana.class)
@RunWith(PowerMockRunner.class)
public class BananaTest {
    @Testpublic void testMethod() throws Exception {
    Banana banana = PowerMockito.spy(new Banana());// 返回正常的值System.out.println(banana.getBananaInfo());// mock私有方法PowerMockito.when(banana, "flavor").thenReturn("不甜了");System.out.println(banana.getBananaInfo());// 修改静态常量Whitebox.setInternalState(Banana.class, "COLOR", "红色");System.out.println(Banana.getColor());// mock静态方法PowerMockito.mockStatic(Banana.class);PowerMockito.when(Banana.getColor()).thenReturn("绿色");System.out.println(Banana.getColor());// mock夫类私有变量MemberModifier.field(Banana.class, "fruit").set(banana, "蔬菜");System.out.println(banana.getFruit());// mock fina方法PowerMockito.when(banana.isLike()).thenReturn(true);}
}

上面我们有说到使用PowerMock就必须加@RunWith(PowerMockRunner.class),但是我们毕竟有时会使用多个测试框架,可能@RunWith会占用。这时我们可以使用@Rule。代码如下:

@Rule
public PowerMockRule rule = new PowerMockRule();/*** 这种方法需要加入依赖* testCompile "org.powermock:powermock-module-junit4-rule:1.7.3"* testCompile "org.powermock:powermock-classloading-xstream:1.7.3"*/
  相关解决方案