zoukankan      html  css  js  c++  java
  • Hamcrest 总结

    Junit

    JUnit框架用一组assert方法封装了一些常用的断言。这些assert方法可以帮我们简化单元测试的编写。这样的话,Junit就可以根据这些断言是否抛出 AssertionFailedError 错误来判断测试用例的执行结果。

     

    Hamcrest

    使用过Junit 的应该有过体验:在实际开发中,一些基本的断言,如eqaul,null,true它们的可读性并不是很好。而且很多时候我们要比较对象、集合、Map等数据结构。这样我们要么进行大段的字段获取再断言。或者干脆自己编写表达式并断言其结果。
    JUnit4.4引入了Hamcrest框架,Hamcest提供了一套匹配符Matcher,这些匹配符更接近自然语言,可读性高,更加灵活。

    Hamcrest 提供了大量被称为“匹配器”的方法。其中每个匹配器都设计用于执行特定的比较操作。Hamcrest的可扩展性很好,让你能够创建自定义的匹配器。最重要的是,JUnit也包含了Hamcrest的核心,提供了对Hamcrest的原生支持,可以直接使用Hamcrest。当然要使用功能齐备的Hamcrest,还是要引入对它的依赖。
     

    看个对比例子,前者使用Junit的 断言,后者使用 Hamcrest的断言。

    1. @Test
    2. public void test_with_junit_assert() {
    3. int expected = 51;
    4. int actual = 51;
    5. assertEquals("failure - They are not same!", expected, actual);
    6. }
    7. @Test
    8. public void test_with_hamcrest_assertThat() {
    9. int expected = 51;
    10. int actual = 51;
    11. assertThat("failure - They are not same!", actual, equalTo(expected));
    12. }

    个人感觉有两个明显的区别:
    1. 参数顺序。两者的expected 和 actual 前后顺序是相反的。
    2. Hamcrest 几乎总是直接使用对象。它的语法更符合函数式编程的风格。
    这点很好理解了,Junit 总是获取值后再比较,因为比较的是简单的值,因此被比较的放在前面更符合习惯。
    而Hamcrest 是直接对测试结果的对象进行一些更复杂的匹配。

    支持语言

    Hamcrest 支持以下几种语言,详情见http://hamcrest.org/
    Java
    Python
    Ruby
    Objective-C
    PHP
    Erlang
    Swift

    Hamcrest匹配器

    Hamcrest 提供了很强大的一些api 供我们进行测试断言。

    1. 核心:
    2. anything - 总是匹配,如果你不关心测试下的对象是什么是有用的
    3. describedAs - 添加一个定制的失败表述装饰器
    4. is - 改进可读性装饰器 - 见下 “Sugar”
    5. 逻辑:
    6. allOf - 如果所有匹配器都匹配才匹配,像Java里的&&
    7. anyOf - 如果任何匹配器匹配就匹配,像Java里的||
    8. not - 如果包装的匹配器不匹配器时匹配,反之亦然
    9. 对象:
    10. equalTo - 测试对象相等使用Object.equals方法
    11. hasToString - 测试Object.toString方法
    12. instanceOf, isCompatibleType - 测试类型
    13. notNullValue, nullValue - 测试null
    14. sameInstance - 测试对象实例
    15. Beans:
    16. hasProperty - 测试JavaBeans属性
    17. 集合:
    18. array - 测试一个数组元素test an array’s elements against an array of matchers
    19. hasEntry, hasKey, hasValue - 测试一个Map包含一个实体,键或者值
    20. hasItem, hasItems - 测试一个集合包含一个元素
    21. hasItemInArray - 测试一个数组包含一个元素
    22. 数字:
    23. closeTo - 测试浮点值接近给定的值
    24. greaterThan, greaterThanOrEqualTo, lessThan, lessThanOrEqualTo - 测试次序
    25. 文本:
    26. equalToIgnoringCase - 测试字符串相等忽略大小写
    27. equalToIgnoringWhiteSpace - 测试字符串忽略空白
    28. containsString, endsWith, startsWith - 测试字符串匹配

    这些API 几乎覆盖了我们测试断言的所有情况。再提供良好阅读性的情况下,减少了一些取值、循环、类型判断等代码的编写。
     

    不足

    这里说的不足,只是本人使用过程中遇到的问题。如果大牛发现Hamcrest 现有的api 支持,欢迎指正。

    hasProperty

    先看一个复杂的层级结构。

    1. public class MyPerson {
    2. private Address address;
    3. private String name;
    4. private String fullName;
    5. // ...
    6. }

    其中 Address 类如下:

    1. public class Address {
    2. private String companyAddress;
    3. private String personalAddress;
    4. // ...
    5. }

    这里我们测试一个person对象,有 companyAddress 属性。

    1. @Test
    2. public void test_with_complex_property() {
    3. MyPerson p = new MyPerson();
    4. Address address = new Address();
    5. address.setCompanyAddress("");
    6. p.setAddress(address);
    7. assertThat("failure has no address property !", p, hasProperty("address"));
    8. // would failed
    9. // assertThat("failure has no address.companyAddress !", p, hasProperty("address.companyAddress"));
    10. }

    这个单元测试的第二个断言出错。也就是说断言 hasProperty 时,不能跨层。这点就没有 rest-assured 的API 用着那么方便了。

    这里我想了个办法如下:

    assertThat("failure has no address property !", p, hasProperty("address", hasProperty("companyAddress")));

    进行了一次嵌套后,就可以满足我们的需要了。

    集合的泛型

    这里我们要处理集合,断言每个元素都有属性:

    1. @Test
    2. public void test_with_list_generics() {
    3. List persons = new ArrayList<MyPerson>();
    4. MyPerson p = new MyPerson();
    5. p.setName("KaKa");
    6. persons.add(p);
    7. MyPerson p2 = new MyPerson();
    8. p2.setName("Hust");
    9. persons.add(p2);
    10. // compile error
    11. // assertThat("failure has no address property !", persons, everyItem(hasProperty("address")));
    12. assertThat("failure has no address property !", (List<Object>)persons, everyItem(hasProperty("address")));
    13. }

    这我就没法理解了。显然,被注释的那句断言直接编译失败!然后向上转型后,反而成功。

    对象自身属性比较

    还用上面的例子,每个 person对象 都有 namefullName 两个属性,断言 fullName 是startwith name 属性。

    1. @Test
    2. public void test_with_list_compare_with_self() {
    3. // 加上 类型
    4. List persons = new ArrayList<MyPerson>();
    5. MyPerson p = new MyPerson();
    6. p.setName("KaKa");
    7. p.setFullName("KaKa Zhang");
    8. persons.add(p);
    9. MyPerson p2 = new MyPerson();
    10. p2.setName("Hust");
    11. p.setFullName("Hust Zhang");
    12. persons.add(p2);
    13. // 这里没法写, person对象 属性 startsWith 另一个属性
    14. assertThat((List<Object>)persons, everyItem(hasProperty("name", startsWith(""))));
    15. }

    这时候只能循环来断言,某个属性startwWith 另一个属性了。

    1. @Test
    2. public void test_with_list_compare_with_self() {
    3. // 加上 类型
    4. List<MyPerson> persons = new ArrayList<MyPerson>();
    5. MyPerson p = new MyPerson();
    6. p.setName("KaKa");
    7. p.setFullName("KaKa Zhang");
    8. persons.add(p);
    9. MyPerson p2 = new MyPerson();
    10. p2.setName("Hust");
    11. p2.setFullName("Hust Zhang");
    12. persons.add(p2);
    13. // 还是用循环来做
    14. for (MyPerson person : persons) {
    15. assertThat(person.getFullName().startsWith(""), is(true));
    16. }
    17. }

    上面的这几个时我遇到几个情况。如果大牛有更好的做法,欢迎指点。
     

          </div>
  • 相关阅读:
    JS正则表达式
    TortoiseGit 中文汉化
    java之 Mybatis框架
    正向代理和反向代理
    Java基础之 多线程
    python库参考学习网址
    postman进行https接口测试所遇到的ssl证书问题,参考别人方法
    hbase单机搭建
    搭建单机版伪分布zookeeper集群
    搭建hadoop单机版
  • 原文地址:https://www.cnblogs.com/jpfss/p/9834982.html
Copyright © 2011-2022 走看看