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

2023-11-17

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

一、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:00
        val 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
     */
    @Test
    fun testIsNotNull() {
        val person1 = mock(Person::class.java)
        Assert.assertNotNull(person1)
    }

    /**
     * 方法2 注解方法
     */
    @Mock
    lateinit var person2: Person

    @Before
    fun setUp() {
        MockitoAnnotations.initMocks(this)
    }

    @Test
    fun testIsNotNull2() {
        Assert.assertNotNull(person2)
    }

    /**
     * 方法3 MockitoRule
     */
    @Mock
    lateinit var person3: Person

    @Rule
    public var mockitoRule = MockitoJUnit.rule()

    @Test
    fun 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: String
    val mSex: String

    init {
        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)
	@Test
    fun testPersonVerifyAfter() {
        Mockito.`when`(person2.getName()).thenReturn("jack")
        // 延时一秒验证
        println(person2.getName())
        println(System.currentTimeMillis())
        verify(person2, after(1000)).getName()
        println(System.currentTimeMillis())
    }

    @Test
    fun testPersonVerifyAtLeast() {
        person2.getSex()
        person2.getSex()
        // 至少验证2次
        verify(person2, atLeast(2)).getSex()
        // 该方法验证了两次
        verify(person2, times(2)).getSex()
    }
    
	// 常用参数匹配器
	@Test
    fun 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.testdateunit

abstract class Fruit {
    private val fruit = "水果"

    open fun getFruit(): String {
        return fruit
    }
}

open class Banana: Fruit() {

     companion object {
         private val COLOR = "黄色的"

         @JvmStatic
         fun 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 {
    @Test
    public 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"
     */
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JUnit->Mockito->PowerMock->持续更新 的相关文章

  • Android 通知进度条冻结

    这是我正在使用的代码 http pastebin com 3bMCKURu http pastebin com 3bMCKURu 问题是 一段时间后 文件变得更重 通知栏下拉速度变慢 最后它就冻结了 你的通知太频繁了 这就是它冻结的原因 让
  • Android 应用程序在后台运行时保存数据

    目前我正在开发 xmmp 客户端 当应用程序位于前台时 该客户端工作得很好 但由于事实上 当应用程序处于后台时 我在 Application 类中保存了大量数据 复杂的 ArrayList 字符串和布尔值作为公共静态 每个字段都被垃圾收集
  • 如何更新 Firebase 中的节点密钥?

    如何重命名14 04 2017 node 没有用于重命名节点的 API 您必须获取节点的值 使用新名称将其保存到数据库并删除旧节点
  • 如何将安卓手机从睡眠状态唤醒?

    如何以编程方式将 Android 手机从睡眠状态唤醒 挂起至内存 我不想获取任何唤醒锁 这意味着手机在禁用 CPU 的情况下进入 真正的 睡眠状态 我想我可以使用某种RTC 实时时钟 机制 有人有例子吗 Thanks 为了让Activity
  • 在自定义对象中创建时粘性服务不会重新启动

    我有一个具有绑定服务的单例对象 我希望它重新启动 当我从启动器启动应用程序时 单例对象将初始化并绑定到这个现有的服务实例 以下是在单例中创建和绑定服务的代码 public class MyState private static MySta
  • Phonegap - 如何将.txt文件保存在Android手机的根目录中

    我正在尝试使用phonegap 将 txt 文件保存在Android 手机的根目录中 我已经安装了这些插件 cordova plugin file 和 cordova plugin file transfer 在 config xml 文件
  • Android libgdx 首选项丢失

    我在 Libgdx 引擎中创建了 Android 游戏 一段时间后 我注意到在某些应用程序杀手中杀死该应用程序后 或者如果我在 Android 设置中执行 强制关闭 操作 我保存到首选项中的游戏选项就会丢失 有办法防止这种情况吗 我从来没有
  • RxJava、Proguard 和 sun.misc.Unsafe

    我有以下问题RxJava 1 1 0 使用时Proguard 我没有更改 RxJava 版本或其 pro文件 但更新后OkHttp我无法编译使用Proguard因为我有关于sun misc Unsafe不在场 rxJava pro keep
  • Android Studio 在编译时未检测到支持库

    由于 Android Studio 将成为 Android 开发的默认 IDE 因此我决定将现有项目迁移到 Android studio 中 项目结构似乎不同 我的项目中的文件夹层次结构如下 Complete Project gt idea
  • TextView 之间有分隔线

    我正在尝试在 android studio 中创建以下布局 因为我对 android 东西还很陌生 所以我第一次尝试使用 LinearLayout 并认为这可能无法实现 现在我正在尝试使用RelativeLayout 我已经用颜色创建了这个
  • Firebase:如何在Android应用程序中设置默认通知渠道?

    如何设置default通知渠道通知消息当应用程序在后台运行时会出现什么情况 默认情况下 这些消息使用 杂项 通道 如你看到的在官方文档中 https firebase google com docs cloud messaging andr
  • Flutter 深度链接

    据Flutter官方介绍深层链接页面 https flutter dev docs development ui navigation deep linking 我们不需要任何插件或本机 Android iOS 代码来处理深层链接 但它并没
  • MediaCodec 创建输入表面

    我想使用 MediaCodec 将 Surface 编码为 H 264 使用 API 18 有一种方法可以通过调用 createInputSurface 然后在该表面上绘图来对表面中的内容进行编码 我在 createInputSurface
  • 使用 Matrix.setPolyToPoly 选择位图上具有 4 个点的区域

    我正在 Android 上使用位图 在使用 4 个点选择位图上的区域时遇到问题 并非所有 4 点组都适合我 在某些情况下 结果只是一个空白位图 而不是裁剪后的位图 如图所示 并且 logcat 中没有任何错误 甚至是内存错误 这是我用来进行
  • 材质设计图标颜色

    应该是哪种颜色 暗 材质图标 在官方文档上 https www google com design spec style icons html icons system icons https www google com design s
  • 通过 ADB 拔出设备:“找不到服务”

    我必须测试我的应用程序在打瞌睡模式下的行为 根据文档 https developer android com training monitoring device state doze standby html testing doze 我
  • Android:有没有办法以毫安为单位获取设备的电池容量?

    我想获取设备的电池容量来进行一些电池消耗计算 是否可以以某种方式获取它 例如 三星 Galaxy Note 2 的电池容量为 3100mAh 谢谢你的帮助 知道了 在 SDK 中无法直接找到任何内容 但可以使用反射来完成 这是工作代码 pu
  • 使用 Espresso 检查 EditText 的字体大小、高度和宽度

    如何使用 Espresso 检查 EditText 的字体大小 高度和宽度 目前要分割我使用的文本 onView withId R id editText1 perform clearText typeText Amr 并阅读文本 onVi
  • 无法运行我的应用程序,要求选择 Android SDK

    今天我已经安装了Android Studio 金丝雀 1 现在我无法运行我的应用程序 将出现以下对话框 我已经通过 文件 gt 项目结构 gt Android SDK 位置 设置了正确的 SDK 位置 期待您的帮助来解决这个问题 警告对话框
  • 找到 Android 浏览器中使用的 webkit 版本?

    有没有办法知道某些特定手机上的 Android 浏览器使用的是哪个版本的 webkit 软件 如果有一个您可以浏览以获取该信息的 URL 那就太好了 但任何其他方式也很好 如果你知道 webkit 版本 你就知道 html5 支持多少 至少

随机推荐