`
haibin369
  • 浏览: 58506 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

JUnit4学习笔记(三):assertThat语法与Matcher

    博客分类:
  • Java
阅读更多

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

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

public class AssertThatTest {
    private int id = 6;
    private boolean trueValue = true;
    private Object nullObject = null;
    private String msg = "Hello World";

    @Test
    public void testAssert() throws Exception {
        assertEquals(6, id);
        assertTrue(trueValue);
        assertNull(nullObject);
        assertTrue(msg != null && msg.startsWith("Hello") && msg.endsWith("World"));
    }
}

 但是这些基本的断言有些可读性并不是很好,例如上面最后一个断言,判断一个字符串以“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重写上述方法:

public class AssertThatTest {
    private int id = 6;
    private boolean trueValue = true;
    private Object nullObject = null;
    private String msg = "Hello World!";

    @Test
    public void testAssertThat() throws Exception {
        //由于静态导入了org.haibin369.matcher.MyMatchers.*,可以调用里面的
        //is(), nullValue(), containsString(), startsWith()方法,可读性更好
        assertThat(id, is(6));
        assertThat(trueValue, is(true));
        assertThat(nullObject, nullValue());
        assertThat(msg, both(startsWith("Hello")).and(endsWith("World")));
    }
}

 重写后的测试和之前的效果一模一样,但是可读性更好了,最后一个断言,能一眼看出来是要以“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接口的源码

/**
 * A matcher over acceptable values.
 * A matcher is able to describe itself to give feedback when it fails.
 * <p/>
 * Matcher implementations should <b>NOT directly implement this interface</b>.
 * Instead, <b>extend</b> the {@link BaseMatcher} abstract class,
 * which will ensure that the Matcher API can grow to support
 * new features and remain compatible with all Matcher implementations.
 * <p/>
 * For easy access to common Matcher implementations, use the static factory
 * methods in {@link CoreMatchers}.
 * <p/>
 * N.B. Well designed matchers should be immutable.
 * 
 * @see CoreMatchers
 * @see BaseMatcher
 */
public interface Matcher<T> extends SelfDescribing {

    boolean matches(Object item);
    
    void describeMismatch(Object item, Description mismatchDescription);

    @Deprecated
    void _dont_implement_Matcher___instead_extend_BaseMatcher_();
}

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

public abstract class BaseMatcher<T> implements Matcher<T> {

    /**
     * @see Matcher#_dont_implement_Matcher___instead_extend_BaseMatcher_()
     */
    @Override
    @Deprecated
    public final void _dont_implement_Matcher___instead_extend_BaseMatcher_() {
        // See Matcher interface for an explanation of this method.
    }

    @Override
    public void describeMismatch(Object item, Description description) {
        description.appendText("was ").appendValue(item);
    }

    @Override
    public String toString() {
        return StringDescription.toString(this);
    }
}

 编写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");
    }
}

 

执行测试:

public class AssertThatTest {
    User user = new User("haibin369", "123456");

    @Test
    public void testAdmin() throws Exception {
        assertThat(user, new IsAdminMatcher());
    }
}

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

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

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

/**
 * 当断言失败时,描述实际上得到的错误的对象。
 */
@Override
public void describeMismatch(Object item, Description description) {
    if (item == null) {
        description.appendText("was null");
    } else {
        User user = (User) item;
        description.appendText("was a common user (")
                .appendText("username: ").appendText(user.getUsername()).appendText(", ")
                .appendText("password: ").appendText(user.getPassword()).appendText(")");
    }
}

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

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

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

public class MyMatchers {
    public static Matcher<User> isAdmin() {
        return new IsAdminMatcher();
    }
}

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

import static org.haibin369.matcher.MyMatchers.*;

public class AssertThatTest {

    User user = new User("haibin369", "123456");

    @Test
    public void testAdmin() throws Exception {
        assertThat(user, isAdmin());
    }
}

 

 

分享到:
评论

相关推荐

    junit4单元测试报错:method initializationerror not found

    junit4单元测试报错:method initializationerror not found 缺少jar包导致,总共需要三个jar包:junit-4.11.jar,hamcrest-core-1.3.rc2.jar,hamcrest-library-1.3.rc2.jar,都在压缩包里了。

    system-rules-1.18.0.rar所需junit4.jar依赖包

    JUnit 4所需system-rules.jar依赖包,主要包含:system-rules-1.16.1.jar,system-rules-1.17.1.jar,system-rules-1.18.0.jar

    junit-jupiter-engine-5.8.2-API文档-中文版.zip

    赠送jar包:junit-jupiter-engine-5.8.2.jar; 赠送原API文档:junit-jupiter-engine-5.8.2-javadoc.jar; 赠送源代码:junit-jupiter-engine-5.8.2-sources.jar; 赠送Maven依赖信息文件:junit-jupiter-engine-...

    junit4学习文档

    junit4学习文档,有实例,非常详细,不懂的可以参考

    JUnit学习笔记JUnit学习笔记JUnit学习笔记

    JUnit是一款由Erich Gamma(《设计模式》的作者)和Kent Beck(极限编程的提出者)编写的开源的回归测试...与早期的JUnit 3相比,JUnit 4.1依赖于Java 5.0的新特性,因此无法兼容于jdk 1.4,可以说是一个全新的框架。

    JUnit4学习笔记

    JUnit4是JUnit框架有史以来的最大改进,其主要目标便是利用Java5的Annotation特性简化测试用例的编写。 先简单解释一下什么是Annotation,这个单词一般是翻译成元数据。元数据是什么?元数据就是描述数据的数据。也...

    Junit学习笔记~

    Junit学习笔记,希望有用~~~~~~~~~~~~~~~~~~~~~~

    初试android studio 创建project时报错:Could not download junit.jar(junit:junit:4.12)

    本人小白,开始学习Android,记录自己的错误瞬间,大神不要喷我,哈哈! 安装好android studio后,测试并创建了一个HelloWord project,按照提示一步一步设置完成后,发现要下载一些gradle resource,结果下载了一夜...

    junit-vintage-engine-5.6.2.jar_junit testng

    junit-vintage-engine-5.6.2.jarjunit-vintage-engine-5.6.2.jarjunit-vintage-engine-5.6.2.jar

    Junit4学习笔记—入门篇.pdf

    以一个简单的DEMO介绍如何在Eclipse下使用JUnit4。供初次接触JUnit的同学学习

    junit-jupiter-api-5.5.2-API文档-中文版.zip

    赠送jar包:junit-jupiter-api-5.5.2.jar; 赠送原API文档:junit-jupiter-api-5.5.2-javadoc.jar; 赠送源代码:junit-jupiter-api-5.5.2-sources.jar; 赠送Maven依赖信息文件:junit-jupiter-api-5.5.2.pom; ...

    junit-jupiter-engine-5.8.0-M1-API文档-中文版.zip

    赠送jar包:junit-jupiter-engine-5.8.0-M1.jar; 赠送原API文档:junit-jupiter-engine-5.8.0-M1-javadoc.jar; 赠送源代码:junit-jupiter-engine-5.8.0-M1-sources.jar; 赠送Maven依赖信息文件:junit-jupiter-...

    junit个人学习笔记

    现在的版本都4.3.1,3.8.1的感觉好象不好找,虽然www.junit.org能够找到,但对我们这种英语不好的来说,还是不好找,再说了,现在好多的教程都是以3.8.1来写的,所以在这里把我找到的3.8.1传上来供大家以后下载 博文链接:...

    junit-jupiter-api-5.4.2-API文档-中英对照版.zip

    赠送jar包:junit-jupiter-api-5.4.2.jar; 赠送原API文档:junit-jupiter-api-5.4.2-javadoc.jar; 赠送源代码:junit-jupiter-api-5.4.2-sources.jar; 赠送Maven依赖信息文件:junit-jupiter-api-5.4.2.pom; ...

    Junit学习笔记

    Junit学习笔记和课件,欢迎学习爱好者下载资源,共同学习。。

    Junit4.12和依赖包

    使用Junit4.12需要用的两个包,官网在国内无法下载

    实验三:junit测试.rar

    实验三:junit测试.rar实验三:junit测试.rar

    JUnit学习笔记

    NULL 博文链接:https://greatjone.iteye.com/blog/1161009

Global site tag (gtag.js) - Google Analytics