• Junit4X系列--hamcrest的使用



    OK,在前面的一系列博客里面,我整理过了Assert类下面常用的断言方法,比如assertEquals等等,但是org.junit.Assert类下还有一个方法也用来断言,而且更加强大。这就是我们这里要这里的:

    Assert的AssertThat()方法和Hamcrest匹配器

    1,断言抛出的异常

    明显的,有的时候我们想测试我们的代码在某种情况下抛出异常。比如说对于无效输入,我们希望代码抛出IllegalArgumentException。前面我也已经说过了,可以使用Test注解的一个expected属性来遇见我们抛出的异常。代码如下:
    package test.junit4test;
    
    import org.junit.Test;
    
    public class LinkinTest
    {
    
    	@Test(expected=NullPointerException.class)
    	public void test()
    	{
    		String str = null;
    		System.out.println(str.toString());
    	}
    }
    这是一种检查异常的简洁方式。但是有时候我们想更加具体的了解抛出的具体异常。考虑如下情景:现在我们除了想知道我们抛出的异常属于那种类型,我们还想检查抛出的异常中“message”中携带的信息。那么怎么办呢?
    那么没办法,让我们返回到古老的try和catch吧。然后在catch块中我们来使用Assert类的assertThat()方法和强大的Hamcrest匹配器来处理吧。代码如下:
    package test.junit4test;
    
    import org.hamcrest.Matchers;
    import org.junit.Assert;
    import org.junit.Test;
    
    public class LinkinTest
    {
    
    	@Test
    	public void test()
    	{
    		try
    		{
    			throw new Exception("吆西,这里应该抛出异常的呢。。。");
    		}
    		catch (Exception e)
    		{
    			Assert.assertThat(e.getMessage(), Matchers.containsString("吆西"));
    		}
    	}
    }

    2,现在让我们来认真的看下assertThat()和Hamcrest匹配器吧
    Assert类的assertThat()方法源码:

    public static <T> void assertThat(T actual, Matcher<? super T> matcher) {
            assertThat("", actual, matcher);
        }
    上面的这个断言方法最后一个参数要传入一个matcher,这个方法是一个钩子,允许程序员自行扩展基本的断言,或者使用第三方的匹配器库。
    使用Hamcrest要导入hamcrest-junit包的,注意这个包可不是junit4.12自带的那个hamcrest-core这个jar。
    junit4.12自带hamcrest-core的依赖,我们来看下junit4.12的pom文件:
    <dependencies>
    		<dependency>
    			<groupId>org.hamcrest</groupId>
    			<artifactId>hamcrest-core</artifactId>
    			<version>1.3</version>
    		</dependency>
    	</dependencies>

    OK,那我们现在导入hamcrest-junit包,项目中pom文件如下:
    <dependencies>
    		<dependency>
    			<groupId>junit</groupId>
    			<artifactId>junit</artifactId>
    			<version>4.12</version>
    			<scope>test</scope>
    		</dependency>
    		<dependency>
    			<groupId>org.hamcrest</groupId>
    			<artifactId>hamcrest-junit</artifactId>
    			<version>2.0.0.0</version>
    			<scope>test</scope>
    		</dependency>
    	</dependencies>

    3,现在我们来看下Hamcrest的api

    ========================================================================================================================================================================================

    A tour of common matchers

    Hamcrest comes with a library of useful matchers. Here are some of the most important ones.

    • Core
      • anything - always matches, useful if you don't care what the object under test is
      • describedAs - decorator to adding custom failure description
      • is - decorator to improve readability - see "Sugar", below
    • Logical
      • allOf - matches if all matchers match, short circuits (like Java &&)
      • anyOf - matches if any matchers match, short circuits (like Java ||)
      • not - matches if the wrapped matcher doesn't match and vice versa
    • Object
      • equalTo - test object equality using Object.equals
      • hasToString - test Object.toString
      • instanceOfisCompatibleType - test type
      • notNullValuenullValue - test for null
      • sameInstance - test object identity
    • Beans
      • hasProperty - test JavaBeans properties
    • Collections
      • array - test an array's elements against an array of matchers
      • hasEntryhasKeyhasValue - test a map contains an entry, key or value
      • hasItemhasItems - test a collection contains elements
      • hasItemInArray - test an array contains an element
    • Number
      • closeTo - test floating point values are close to a given value
      • greaterThangreaterThanOrEqualTolessThanlessThanOrEqualTo - test ordering
    • Text
      • equalToIgnoringCase - test string equality ignoring case
      • equalToIgnoringWhiteSpace - test string equality ignoring differences in runs of whitespace
      • containsStringendsWithstartsWith - test string matching
    ========================================================================================================================================================================================
    OK,下面我写了一个例子,让我们来看下Hamcrest匹配器的使用。
    package org.linkinpark.junit.testjunit;
    
    import static org.junit.Assert.assertThat;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import org.hamcrest.Matchers;
    import org.junit.Before;
    import org.junit.Test;
    
    public class LinkinTest
    {
    	private Linkin linkin;
    	private Linkin linkin1;
    	private Linkin linkin2;
    	private List<Linkin> list = new ArrayList<>(3);
    	private Map<String, Linkin> map = new HashMap<>();
    	String[] strArray = { "1", "2" };
    
    	@Before
    	public void setUp()
    	{
    		linkin = new Linkin();
    		linkin1 = new Linkin();
    		linkin2 = linkin1;
    		list.add(linkin);
    		list.add(linkin1);
    		list.add(linkin2);
    		map.put("linkin", linkin);
    		map.put("linkin1", linkin1);
    		map.put("linkin2", linkin2);
    	}
    
    	@SuppressWarnings("unchecked")
    	@Test
    	public void test() throws Exception
    	{
    		/******************* 对象相关方法 ********************************/
    		// equalTo:判断2个对象是否相等,使用Object.equals方法
    		assertThat(linkin1, Matchers.equalTo(linkin2));
    		// hasToString:判断一个对象的toString方法
    		assertThat(linkin, Matchers.hasToString("Linkin [name=null, age=null]"));
    		// instanceOf:判断对象是否为某个类的实例对象
    		assertThat(linkin, Matchers.instanceOf(Linkin.class));
    		// notNullValue,nullValue:判断对象是否为null值
    		assertThat(null, Matchers.nullValue());
    		assertThat(linkin, Matchers.notNullValue());
    		// sameInstance: 测试2个对象是否同一个实例
    		assertThat(linkin1, Matchers.sameInstance(linkin2));
    
    		/******************* javaBean相关方法 ****************************/
    		assertThat(linkin, Matchers.hasProperty("name"));
    
    		/******************* 集合相关方法 ********************************/
    		assertThat(strArray, Matchers.array(Matchers.equalTo("1"), Matchers.equalTo("2")));
    		// hasEntry, hasKey, hasValue:测试一个Map包含一个实体,键或者值
    		assertThat(map, Matchers.hasEntry("linkin", linkin));
    		assertThat(map, Matchers.hasKey("linkin"));
    		assertThat(map, Matchers.hasValue(linkin));
    		// hasItem, hasItems:测试一个集合包含一个元素
    		assertThat(list, Matchers.hasItem(linkin));
    		assertThat(list, Matchers.hasItems(linkin, linkin1));
    		// hasItemInArray:测试一个数组包含一个元素
    		assertThat(strArray, Matchers.hasItemInArray("1"));
    		// in:测试一个对象在一个集合中
    		assertThat(linkin, Matchers.in(list));
    
    		/******************* 数字相关方法 ********************************/
    		// closeTo:测试浮点值接近给定的值
    		assertThat(1.5, Matchers.closeTo(1.0, 0.6));
    		// greaterThan, greaterThanOrEqualTo, lessThan, lessThanOrEqualTo:测试大于,小于
    		assertThat(1.0, Matchers.greaterThan(0.5));
    		assertThat(1.5, Matchers.lessThanOrEqualTo(1.5));
    
    		/******************* 文本相关方法 ********************************/
    		// equalToIgnoringCase:测试字符串相等忽略大小写
    		assertThat("LinkinPark", Matchers.equalToIgnoringCase("linkinpark"));
    		// equalToIgnoringWhiteSpace:测试字符串忽略空白
    		assertThat("  LinkinPark111", Matchers.equalToIgnoringWhiteSpace("LinkinPark111"));
    		// containsString, endsWith, startsWith:测试字符串匹配
    		assertThat("LinkinPark", Matchers.containsString("Lin"));
    		assertThat("LinkinPark", Matchers.startsWith("Lin"));
    		assertThat("LinkinPark", Matchers.endsWith("Park"));
    
    		/******************* 逻辑相关方法 ********************************/
    		// allOf:如果所有匹配器都匹配才匹配
    		assertThat("LinkinPark", Matchers.allOf(Matchers.endsWith("Park"), Matchers.startsWith("Lin")));
    		// anyOf:如果任何匹配器匹配就匹配
    		assertThat("LinkinPark", Matchers.anyOf(Matchers.endsWith("P22ark"), Matchers.notNullValue()));
    		// not:如果包装的匹配器不匹配器时匹配,反之亦然
    		assertThat("LinkinPark", Matchers.not(Matchers.endsWith("P22ark")));
    		// is:如果包装的匹配器匹配器时匹配,反之亦然
    		assertThat(linkin1, Matchers.is(linkin2));
    		assertThat("LinkinPark", Matchers.is(Matchers.endsWith("Park")));
    	}
    
    }
    

    关于上面的代码解释一下,就代码的可读性来说,我没有静态导入Matchers类下的所有方法,所以代码充斥着大量的该类打点。这里只是我第一次使用Hamcrest匹配器,所以方便我自己调方法才这么写的。以后如果经常用到一些类的静态方法的话那么建议大家都静态导出。比如说使用Assert类调用它里面那些静态方法就应该静态导入。

    4,扩展Hamcrest的Matcher接口自定义匹配器。
    如果内置的断言或者Hamcrest匹配器不足以表达我们的意图的时候,我们可以自己来扩展。我们自己实现自己的匹配器,要实现Hamcrest的Matcher接口。
    这里我举个例子,现在我自定义一个匹配器,来检验一个字符串必须包含“Linkin”这个字符串,代码如下:
    package org.linkinpark.junit.testjunit;
    
    import org.hamcrest.BaseMatcher;
    import org.hamcrest.Description;
    import org.hamcrest.Matcher;
    import org.junit.Assert;
    import org.junit.Test;
    
    public class LinkinTest
    {
    
    	@Test
    	public void test() throws Exception
    	{
    		Assert.assertThat("LinkinPark", isLinkinStr());
    	}
    
    	public Matcher<String> isLinkinStr()
    	{
    		return new BaseMatcher<String>()
    		{
    
    			@Override
    			public boolean matches(Object item)
    			{
    				if (!(item instanceof String))
    				{
    					return false;
    				}
    				return ((String) item).contains("Linkin");
    			}
    
    			@Override
    			public void describeTo(Description description)
    			{
    				description.appendText("字符串必须包含Linkin这个单词。。。");
    			}
    
    		};
    	}
    
    }
    

    OK,junit窗口绿条没问题,如果一个字符串不包含“Linkin”这个单词,那么测试就会报错,我们来看下junit窗口下的显示情况。


    OK,实际编码中一般不会自己来创建自己的匹配器,了解下好了。下一篇我开始整理junit4X的规则和运行器。
  • 相关阅读:
    Visual Studio 2019 使用.Net Core 3.0 一
    Asp.Net真分页技术
    Vue-员工管理系统
    Activex在没有电子秤api的情况下获取串口数据
    C#调用Activex中串口电子秤的数据,并将电子秤的数据显示到前端页面
    C# Datetime.Ticks
    Asp.Net进阶/管家模式+发布订阅模式:练习
    委托解耦
    Asp.Net进阶/值类型与引用类型:复习
    C# 简单日志帮助类LogHelper
  • 原文地址:https://www.cnblogs.com/LinkinPark/p/5232868.html
Copyright © 2020-2023  润新知