zoukankan      html  css  js  c++  java
  • JUnit4学习笔记(三):assertThat语法与Matcher

    一、使用JUnit的一般测试语法

    org.junit.Assert类里有各种断言方法,大部分情况下我们会像下面这个例子一样编写测试:

     1 public class AssertThatTest {  
     2     private int id = 6;  
     3     private boolean trueValue = true;  
     4     private Object nullObject = null;  
     5     private String msg = "Hello World";  
     6   
     7     @Test  
     8     public void testAssert() throws Exception {  
     9         assertEquals(6, id);  
    10         assertTrue(trueValue);  
    11         assertNull(nullObject);  
    12         assertTrue(msg != null && msg.startsWith("Hello") && msg.endsWith("World"));  
    13     }  
    14 }  

     但是这些基本的断言有些可读性并不是很好,例如上面最后一个断言,判断一个字符串以“Hello”开头,以“Workd”结尾,由于没有assertStartWith和assertEndWith之类的函数,我们不得不自己编写表达式并断言其结果。并且因为我们没有提供失败的信息,当这个断言失败时只会抛出java.lang.AssertionError,根本不知道是因为msg为null还是msg的内容错误。

    二、使用assertThat与Matcher

    在org.junit.Assert中除了常用的相等、布尔、非空等断言,还有一种assertThat,需要配合org.hamcrest.Matcher使用,这种断言的语法为:

    assertThat([reason, ]T actual, Matcher<? super T> matcher),其中,reason为断言失败时的输出信息,actual为断言的值或对象,matcher为断言的匹配器,里面的逻辑决定了给定的actual对象满不满足断言。

    在org.hamcrest.CoreMatchers类中组织了所有JUnit内置的Matcher,调用其任意一个方法都会创建一个与方法名字相关的Matcher。

    使用assertThat重写上述方法:

     1 public class AssertThatTest {  
     2     private int id = 6;  
     3     private boolean trueValue = true;  
     4     private Object nullObject = null;  
     5     private String msg = "Hello World!";  
     6   
     7     @Test  
     8     public void testAssertThat() throws Exception {  
     9         //由于静态导入了org.haibin369.matcher.MyMatchers.*,可以调用里面的  
    10         //is(), nullValue(), containsString(), startsWith()方法,可读性更好  
    11         assertThat(id, is(6));  
    12         assertThat(trueValue, is(true));  
    13         assertThat(nullObject, nullValue());  
    14         assertThat(msg, both(startsWith("Hello")).and(endsWith("World")));  
    15     }  
    16 }  

     重写后的测试和之前的效果一模一样,但是可读性更好了,最后一个断言,能一眼看出来是要以“Hello”开头并以“World”结尾的字符串。如果把startsWith("Hello")改成startsWith("Helloo"),它的失败信息也比较直观:

    java.lang.AssertionError:   
    Expected: (a string starting with "Helloo" and a string ending with "World")  
         but: a string starting with "Helloo" was "Hello World!"  

    三、自定义Matcher

    现在我们有一个User对象,只包含两个变量机器setter和getter:username,password,当username和password都为“admin”时表示是管理员(Admin User)。现在我们来创建一个自己的Matcher并运用到assertThat语法中去。

     首先看看org.hamcrest.Matcher接口的源码

     1 /** 
     2  * A matcher over acceptable values. 
     3  * A matcher is able to describe itself to give feedback when it fails. 
     4  * <p/> 
     5  * Matcher implementations should <b>NOT directly implement this interface</b>. 
     6  * Instead, <b>extend</b> the {@link BaseMatcher} abstract class, 
     7  * which will ensure that the Matcher API can grow to support 
     8  * new features and remain compatible with all Matcher implementations. 
     9  * <p/> 
    10  * For easy access to common Matcher implementations, use the static factory 
    11  * methods in {@link CoreMatchers}. 
    12  * <p/> 
    13  * N.B. Well designed matchers should be immutable. 
    14  *  
    15  * @see CoreMatchers 
    16  * @see BaseMatcher 
    17  */  
    18 public interface Matcher<T> extends SelfDescribing {  
    19   
    20     boolean matches(Object item);  
    21       
    22     void describeMismatch(Object item, Description mismatchDescription);  
    23   
    24     @Deprecated  
    25     void _dont_implement_Matcher___instead_extend_BaseMatcher_();  
    26 }  

     类注释上强调,Matcher实现类不应该直接实现这个接口,而应该继承org.hamcrest.BaseMatcher抽象类

     1 public abstract class BaseMatcher<T> implements Matcher<T> {  
     2   
     3     /** 
     4      * @see Matcher#_dont_implement_Matcher___instead_extend_BaseMatcher_() 
     5      */  
     6     @Override  
     7     @Deprecated  
     8     public final void _dont_implement_Matcher___instead_extend_BaseMatcher_() {  
     9         // See Matcher interface for an explanation of this method.  
    10     }  
    11   
    12     @Override  
    13     public void describeMismatch(Object item, Description description) {  
    14         description.appendText("was ").appendValue(item);  
    15     }  
    16   
    17     @Override  
    18     public String toString() {  
    19         return StringDescription.toString(this);  
    20     }  
    21 }  

     编写IsAdminMatcher,需要实现两个方法,第一个是matches,判断给定的对象是否是所期待的值,第二个是describeTo,把应该得到的对象的描述添加进Description对象中。 

    /** 
     * 断言一个给定的User对象是管理员 
     */  
    public class IsAdminMatcher extends BaseMatcher<User> {  
        /** 
         * 对给定的对象进行断言判定,返回true则断言成功,否则断言失败 
         */  
        @Override  
        public boolean matches(Object item) {  
            if (item == null) {  
                return false;  
            }  
      
            User user = (User) item;  
            return "admin".equals(user.getUsername()) && "admin".equals(user.getPassword());  
        }  
      
        /** 
         * 给期待断言成功的对象增加描述 
         */  
        @Override  
        public void describeTo(Description description) {  
            description.appendText("Administrator with 'admin' as username and password");  
        }  
    }  

    执行测试:

    1 public class AssertThatTest {  
    2     User user = new User("haibin369", "123456");  
    3   
    4     @Test  
    5     public void testAdmin() throws Exception {  
    6         assertThat(user, new IsAdminMatcher());  
    7     }  
    8 }  

     测试可以正常执行,但是上面的User对象并不是管理员,因此测试会失败,以下信息会输出:

    1. java.lang.AssertionError:   
    2. Expected: Administrator with 'admin' as username and password  
    3.      but: was <org.haibin369.model.User@570b13e4>  

     查看源代码,我们发现but后面的信息是在BaseMatcher中的describeMismatch方法输出的,通过这个信息明显不清楚到底实际上得到了什么User,因此在我们的Matcher中从写这个方法:

     1 /** 
     2  * 当断言失败时,描述实际上得到的错误的对象。 
     3  */  
     4 @Override  
     5 public void describeMismatch(Object item, Description description) {  
     6     if (item == null) {  
     7         description.appendText("was null");  
     8     } else {  
     9         User user = (User) item;  
    10         description.appendText("was a common user (")  
    11                 .appendText("username: ").appendText(user.getUsername()).appendText(", ")  
    12                 .appendText("password: ").appendText(user.getPassword()).appendText(")");  
    13     }  
    14 }  

     重新执行测试,得到以下失败信息:

    1. java.lang.AssertionError:   
    2. Expected: Administrator with 'admin' as username and password  
    3.      but: was a common user (username: haibin369, password: 123456)  

     虽然我们自定义的Matcher已经能够执行了,但是assertThat(user, new IsAdminMatcher());这段代码并没有达到之前所说的可读性更好的要求,因此,我们仿照org.hamcrest.CoreMatchers,创建一个类去创建我们自定义的Matcher:

    1 public class MyMatchers {  
    2     public static Matcher<User> isAdmin() {  
    3         return new IsAdminMatcher();  
    4     }  
    5 }  

    在测试方法中静态导入该类中的所有内容,则可以像下面一样使用assertThat:

     1 import static org.haibin369.matcher.MyMatchers.*;  
     2   
     3 public class AssertThatTest {  
     4   
     5     User user = new User("haibin369", "123456");  
     6   
     7     @Test  
     8     public void testAdmin() throws Exception {  
     9         assertThat(user, isAdmin());  
    10     }  
    11 }  
  • 相关阅读:
    MyBatis 内连接association 左外连接collection
    mybatis的基本配置:实体类、配置文件、映射文件、工具类 、mapper接口
    SpringMVC 使用Form标签库制作登录表单
    SpringMVC 配置过滤器解决中文乱码问题
    用户登录模块————账户安全问题
    HashSet,TreeSet和LinkedHashSet的区别
    SpringMVC 服务器端验证
    php输出(下载)文件
    26进制(字母)转十进制算法
    PHP之可变变量
  • 原文地址:https://www.cnblogs.com/johnson-blog/p/3890532.html
Copyright © 2011-2022 走看看