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

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

热门文章

    最新讲堂

      • 推荐阅读
      • 换一换
          •   随着产品的不断升级,软件测试人员在研发团队中的比重越来越大,因为前期发展较晚,所以目前这方面的人才缺口很大。  1、测试人员是保证企业赖以生存的关键;  先来看一个因为测试人员的疏漏能给企业造成巨大损失的案例,最有代表性的就是2019年“拼多多100元无门槛消费券”漏洞,由于项目测试不到位,导致很多用户仅用了0.4元就给自己充值了100元的话费,事件虽发生在半夜,但是拼多多依旧损失严重,网络流传损失金额达200亿元,很多人担心拼多多就此倒闭。  这件事也给了企业重重一击,也让他们认识到测试的重要性,这件事不只是个例,每年企业因为测试没有做到位而造成的损失的事件早已屡见不鲜,也再次印证了测试...
            0 0 956
            分享
          • 子查询含义:出现在其它语句中的select语句,称为子查询、内查询、嵌套查询外部的查询语句,称为为主查询或外查询 分类:案子查询出现的位置:select后面      仅支持标量子查询from后面       支持表子查询※where或having后面    标量子查询※、列子查询※、行子查询(较少)exists后面(相关子查询)按结果集的行列数不同:标量子查询(结果集只有一行一列)行子查询(结果集只有一行多列)列子查询(结果集只有一列多行)表子查询(结果集一般为多行多列) 一、whe...
            15 14 1990
            分享
          • 自动化面试题记录整理(部分答案自己整理)1、selenium中如何判断元素是否存在?没有提供原生的方法判断元素是否存在,一般我们可以通过定位元素+异常捕获的方式判断2、selenium中hidden或者是display = none的元素是否可以定位到?不可以,想点击的话,可以用js去掉dispalay=none的属性3、selenium中如何保证操作元素的成功率?也就是说如何保证我点击的元素一定是可以点击的?首先通过封装find方法,实现wait_for_element_ispresent(WebDriverWait)在对页面进行click之前,先滚动到该元素(通过Js封装),避免在页面未加...
            14 13 5589
            分享
          • 一.测试用例的基本要素1.什么是测试用例这个在前面其实已经说过了,具体可以看软件测试的基本概念 这篇博客,这里就简单介绍一下:测试用例是向被测试系统发起的一组集合,包括测试环境,测试步骤,测试数据,预期结果!2.为什么软件测试人员要写测试用例?这里有如下几个原因:测试用例是测试执行的依据;测试用例可以复用,在进行回归测试的时候就不用再重新编写了;测试用例可以衡量需求的覆盖率;后人可以借鉴;手工测试用例是自动化测试的依据二.测试用例的设计方法1.基于需求设计测试用例需求是测试人员进行测试的依据,测试人员分析需求,验证需求的合理性和正确性,无二义性,从需求当中提取出测试项,根据测试项进行进一步的细...
            0 0 1753
            分享
      • 51testing软件测试圈微信