• 13
  • 13
分享
  • Android单元测试(一):JUnit框架的使用——软件测试圈
  • TIMI 2021-07-29 11:18:56 字数 5632 阅读 1266 收藏 13

1.前言

网上有许多关于单元测试的好处,这里我就不去说了。我写单元测试的理由很简单粗暴,就是图一个方便。试想一下这个场景:我们在写一个新功能,每写一部分,我们就安装到手机上查看一下,这个过程中你要点击到对应的页面,做对应的操作,最后才能反馈给你结果。如果达到了预期效果,那么恭喜你。可是一旦这次失败了,是不是又要重复这一过程?是不是感到很麻烦?很费时间?如果你想早点写完下班,那么你就需要掌握单元测试。因为它能大大的缩短你自我验证的时间。

2.准备工作

我们新建一个项目,模板代码会默认在build文件中添加JUnit的依赖,而单元测试代码是放在src/test/java下面的,如下图:

1.jpg

用鼠标右键点击测试方法,选择菜单中的“Run”选项就可以执行对应的单元测试。我执行了图中的测试代码,可以看到执行方法只用了6毫秒,整个过程不到2秒。

3.JUnit介绍

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

使用时在app的build文件中添加依赖。注意:用于测试环境框架一律是testCompile开头。

dependencies {
    testCompile 'junit:junit:4.12'
}

Assert类中主要方法如下:

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

注意:上面的每一个方法,都有对应的重载方法,可以在前面加一个String类型的参数,表示如果断言失败时的提示。

JUnit 中的常用注解:

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

执行顺序:@BeforeClass –> @Before –> @Test –> @After –> @AfterClass

4.JUnit用法

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

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

1.基础用法

1、首先测试stampToDate方法,测试时我认为返回的结果等于“预期时间”这个字符串。测试方法执行后如下图:

2.jpg

可以看到预期值与实际结果不符,测试失败!想要测试成功要么预期值为”2017-10-15 16:00:02”要么使用assertNotEquals方法断言。

2.接下来测试dateToStamp方法。

3.jpg

很简单,我认为返回结果不等于4,结果测试通过。

3.我们注意到在dateToStamp方法中,有抛出一个解析异常(ParseException)。也就是当参数没有按照规定格式去传,就会导致这个异常。

4.jpg

那我们怎么验证一个方法是否抛出了异常呢?可以给@Test注解设置expected参数来实现,如下:

1.jpg

抛出了对应的异常则测试成功,反之则测试失败。

2.参数化测试

这时,你是不是觉得还是很麻烦,因为每次测试一个方法都要去设置对应的值,就不能连续用不不同的值去测试一个方法,省的我们不断地修改。这时就用到了@RunWith与@Parameters。

首先在测试类上添加注解@RunWith(Parameterized.class),在创建一个由 @Parameters 注解的public static方法,让返回一个对应的测试数据集合。最后创建构造方法,方法的参数顺序和类型与测试数据集合一一对应。

1.jpg

上图就是一个简单的例子,可以看到连续执行了三次测试,其中第二次测试没有抛出异常,测试失败!

3.assertThat用法

上面我们所用到的一些基本的断言,如果我们没有设置失败时的输出信息,那么在断言失败时只会抛出AssertionError,无法知道到底是哪一部分出错。而assertThat就帮我们解决了这一点。它的可读性更好。

assertThat(T actual, Matcher<? super T> matcher);
assertThat(String reason, T actual, Matcher<? super T> matcher);

其中reason为断言失败时的输出信息,actual为断言的值,matcher为断言的匹配器。

常用的匹配器整理:

匹配器说明例子
is断言参数等于后面给出的匹配表达式assertThat(5, is (5))
not断言参数不等于后面给出的匹配表达式assertThat(5, not(6))
equalTo断言参数相等assertThat(30, equalTo(30))
equalToIgnoringCase断言字符串相等忽略大小写assertThat(“Ab”, equalToIgnoringCase(“ab”))
containsString断言字符串包含某字符串assertThat(“abc”, containsString(“bc”))
startsWith断言字符串以某字符串开始assertThat(“abc”, startsWith(“a”))
endsWith断言字符串以某字符串结束assertThat(“abc”, endsWith(“c”))
nullValue 断言参数的值为nullassertThat(null, nullValue())
notNullValue断言参数的值不为nullassertThat(“abc”, notNullValue())
greaterThan断言参数大于assertThat(4, greaterThan(3))
lessThan 断言参数小于assertThat(4, lessThan(6))
greaterThanOrEqualTo断言参数大于等于assertThat(4, greaterThanOrEqualTo(3))
lessThanOrEqualTo断言参数小于等于assertThat(4, lessThanOrEqualTo(6))
closeTo断言浮点型数在某一范围内assertThat(4.0, closeTo(2.6, 4.3))
allOf断言符合所有条件,相当于&&assertThat(4,allOf(greaterThan(3), lessThan(6)))
anyOf 断言符合某一条件,相当于或assertThat(4,anyOf(greaterThan(9), lessThan(6)))
hasKey断言Map集合含有此键assertThat(map, hasKey(“key”))
hasValue断言Map集合含有此值assertThat(map, hasValue(value))
hasItem断言迭代对象含有此元素assertThat(list, hasItem(element))

下图为使用assertThat测试失败时所显示的具体错误信息。可以看到错误信息很详细!

1.jpg

当然了匹配器也是可以自定义的。这里我自定义一个字符串是否是手机号码的匹配器来演示一下。

只需要继承BaseMatcher抽象类,实现matches与describeTo方法。代码如下:

public class IsMobilePhoneMatcher extends BaseMatcher<String> {
    /**
     * 进行断言判定,返回true则断言成功,否则断言失败
     */
    @Override
    public boolean matches(Object item) {
        if (item == null) {
            return false;
        }
        Pattern pattern = Pattern.compile("(1|861)(3|5|7|8)\\d{9}$*");
        Matcher matcher = pattern.matcher((String) item);
        return matcher.find();
    }
    /**
     * 给期待断言成功的对象增加描述
     */
    @Override
    public void describeTo(Description description) {
        description.appendText("预计此字符串是手机号码!");
    }
    /**
     * 给断言失败的对象增加描述
     */
    @Override
    public void describeMismatch(Object item, Description description) {
        description.appendText(item.toString() + "不是手机号码!");
    }
}

执行单元测试如下:

正确的手机号码测试成功:

1.jpg

错误号码测试失败:

4.@Rule用法

还记得一开始我们在@Before与@After注解的方法中加入”测试开始”的提示信息吗?假如我们一直需要这样的提示,那是不是需要每次在测试类中去实现它。这样就会比较麻烦。这时你就可以使用@Rule来解决这个问题,它甚至比@Before与@After还要强大。

自定义@Rule很简单,就是实现TestRule 接口,实现apply方法。代码如下:

public class MyRule implements TestRule {
    @Override
    public Statement apply(final Statement base, final Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                // evaluate前执行方法相当于@Before
                String methodName = description.getMethodName(); // 获取测试方法的名字
                System.out.println(methodName + "测试开始!");
                base.evaluate();  // 运行的测试方法
                // evaluate后执行方法相当于@After
                System.out.println(methodName + "测试结束!");
            }
        };
    }
}

我们使用一下我们自定义的MyRule,效果如图:

1.jpg


作者:唯鹿

原文链接:https://blog.csdn.net/qq_17766199/article/details/78243176

  • 【留下美好印记】
    赞赏支持
登录 后发表评论
+ 关注

热门文章

    最新讲堂

      • 推荐阅读
      • 换一换
          •        谈到浏览器兼容性测试,很多同学往往考虑到的都是如何覆盖全面,管它什么Chrome、FireFox、IE、Safari这种主流浏览器还是360、QQ、搜狗等一系列国产浏览器,都给它列到列表上边去,只要时间允许,就一个一个测起来。但是大家有没有想过,这个策略真的是正确的么?真的应该这样去进行测试么?       在回答上面的问题之前,我们先来思考这样一个问题:我们为什么要做兼容性测试? 或者换个说法,网站为什么会在不同的浏览器上展现出不同的样式?毫无疑问,其答案是因为内核不同、渲染方式不同。有了这样的结果...
            5 8 5760
            分享
          •   当涉及到一个网络应用程序时,在它投入生产之前,开发人员必须确保它在所有浏览器中都能正常工作。最终用户应该能够体验能够处理所有关键功能的全功能站点而不考虑最终用户使用的浏览器或设备。应用程序的行为在不同的操作系统、浏览器甚至设备中是不同的,这取决于它们的分辨率。大多数开发人员通常更喜欢在一个浏览器上工作,即使工作站中安装了多个浏览器。  这有时会导致应用程序在其他浏览器中存在bug。在测试阶段,在产品中部署应用程序之前,必须涵盖所有维度。  让我们讨论在以下情况下需要执行的测试策略跨4种主要浏览器测试应用程序,测试人员面临的常见问题以及如何解决这些问题。  Internet Explorer...
            0 0 1007
            分享
          •   测试圈子生态的思考  其实测试的生态,说起来蛮简单的,一个词语概括就是两极分化。有个梗:hand hands,load loads,太贴切了。  两极分化这个词,可以从下面三个维度来看:  薪资  我认识的测试也算不少,月薪上下限从8k到55k不一而足;从年薪角度来说,package10W到100W+的都有,我这里指的是单纯的技术岗位,不算管理岗在内。  而且有个特别有意思的点,绝大多数测试,薪资的天花板就是30K,对应年薪50W以内。这里的绝大多数,我可以给一个用户画像:  工作经验:5-10年;   第一学历:大专-本科;   技术能力:会写点自动化代码,工具仅限于使用,不提造轮子,能...
            0 0 1515
            分享
          • 性能测试工具Loadrunner中监控指标的名词解释 Transactions(用户事务分析)用户事务分析是站在用户角度进行的基础性能分析。1、Transation Sunmmary(事务综述)对事务进行综合分析是性能分析的第一步,通过分析测试时间内用户事务的成功与失败情况,可以直接判断出系统是否运行正常。2、Average Transaciton Response Time(事务平均响应时间)“事务平均响应时间”显示的是测试场景运行期间的每一秒内事务执行所用的平均时间,通过它可以分析测试场景运行期间应用系统的性能走向。例:随着测试时间的变化,系统处理事务的速度开始逐渐变慢,这说明应用系统随着...
            0 0 1875
            分享
          • 1.概述软件测试是指在规定的条件下对程序进行操作,以发现程序错误,衡量软件质量,并对其是否能满足设计要求进行评估的过程。测试案例作为测试执行的依据在软件测试工程中发挥着重要的作用。本文从测试案例的设计意义出发,剖析了测试案例的设计策略、原则和分类,帮助测试人员在进行测试案例设计时,找到案例的设计方向和方法。2.什么是测试案例设计测试案例的设计简单说的就是设计一个测试场景,通过这个测试场景中的输入、执行条件和输出,来判断应用系统是否存在系统缺陷和不足;即通过执行测试案例,来判断系统是否能够正常运行并且达到程序所设计的执行结果。根据测试案例的性质划分,测试案例在设计上可以分为正向测试案例和反向测试...
            0 3 8894
            分享
      • 51testing软件测试圈微信