matchers依赖

本文是我们名为“ 用Mockito测试 ”的学院课程的一部分。

在本课程中,您将深入了解Mockito的魔力。 您将了解有关“模拟”,“间谍”和“部分模拟”的信息,以及它们相应的存根行为。 您还将看到使用测试双打和对象匹配器进行验证的过程。 最后,讨论了使用Mockito的测试驱动开发(TDD),以了解该库如何适合TDD的概念。 在这里查看 !

在本教程中,我们将使用Hamcrest API创建我们自己的自定义匹配器,以扩展Hamcrest提供的“开箱即用”功能。

目录

1.为什么要使用自定义匹配器? 2.匹配器的解剖 3.现有课程的自定义匹配器
3.1。 甚至() 3.2。 divisibleBy(整数除数)
4.针对您自己的班级的自定义匹配器
4.1。 我们的模型:一棵树 4.2。 叶() 4.3。 根() 4.4。 DescendantOf(节点节点) 4.5。 ancestorOf(节点节点) 4.6。 siblingOf(节点节点)
5.结论

1.为什么要使用自定义匹配器?

有时我们会遇到Hamcrest Matchers库的限制。 我们需要在标准类(例如字符串,整数或列表)上具有新的匹配器功能,或者需要创建与已创建的高度自定义类匹配的匹配器。 在本教程中,我们将使用Hamcrest提供的工具针对这两种情况创建匹配器。

2.匹配器的解剖

为了创建自定义匹配器,我们将扩展内置的Abstract类TypeSafeDiagnosingMatcher 。 如果您在IDE中将此类扩展为Integer类型,您将看到该类的两个抽象方法:

public class BlankMatcher extends TypeSafeDiagnosingMatcher<Integer> {@Overrideprotected boolean matchesSafely(Integer integer, Description description) {return false;}@Overridepublic void describeTo(Description description) {}
}

第一个方法matchesSafely()是Matcher的作用所在,这是Hamcrest在要使用Matcher测试值时执行的方法。 如果是这种情况,它还负责报告Matcher为何不匹配。 不匹配描述是Hamcrest在失败匹配器输出的“但是:”部分之后使用的描述,因此不匹配描述应相应地设置格式。

第二种方法describeTo()用于生成匹配器正在检查的内容的描述。 Hamcrest在失败的匹配器的输出的“ Expected:”部分之后使用此描述,因此该描述应相应设置格式。

在后台,此Matcher将检查输入值是否不为null且类型正确,因此在执行命中我们的matchesSafely方法时,我们保证具有正确类型的值以进行匹配。

Hamcrest为我们完成了繁重的工作,因此这两种方法都是实现自己的Hamcrest Matchers所需要的。 在接下来的示例中,我们还将添加一些语法糖,以简化创建和使用自定义匹配器的过程。

3.现有课程的自定义匹配器

在本节中,我们将创建许多可用于现有类型的自定义匹配器。

甚至()

让我们从创建匹配器开始,以确定输入数字是否为偶数。 和以前一样,我们将为整数扩展TypeSafeDiagnosingMatcher

public class IsEven extends TypeSafeDiagnosingMatcher<Integer> {@Overrideprotected boolean matchesSafely(Integer integer, Description description) {return false;}@Overridepublic void describeTo(Description description) {}
}

然后,我们将实现describeTo()方法,以提供对匹配器的期望描述:

@Override
public void describeTo(Description description) {description.appendText("An Even number");
}

我们可以使用description参数来创建Matcher的描述。 Description类提供了许多用于格式化输入的辅助方法,在这种情况下,我们使用appendText()方法简单地添加一些文本。

接下来,让我们使用传递到matchesSafely方法的description参数来创建错误消息。 请记住,只有在故障情况下才能看到此输出。 我们将在Description类中使用几种方法来格式化消息:

@Override
protected boolean matchesSafely(Integer integer, Description description) {description.appendText("was ").appendValue(integer).appendText(", which is an Odd number");return false;
}

接下来,我们将对数字进行实际检查以查看其是否为偶数:

@Override
protected boolean matchesSafely(Integer integer, Description description) {description.appendText("was ").appendValue(integer).appendText(", which is an Odd number");return integer % 2 == 0;
}

最后,我们将向类添加静态工厂方法以使其易于在测试中使用:

public static IsEven isEven() {return new IsEven();
}

总而言之,我们有以下Matcher类:

public class IsEven extends TypeSafeDiagnosingMatcher<Integer> {@Overrideprotected boolean matchesSafely(Integer integer, Description description) {description.appendText("was ").appendValue(integer).appendText(", which is an Odd number");return integer % 2 == 0;}@Overridepublic void describeTo(Description description) {description.appendText("An Even number");}public static IsEven isEven() {return new IsEven();}
}

现在,我们可以使用新的匹配器编写一些测试。

我们创建一个名为IsEvenTest的新Test类,然后导入新的工厂方法:

import static com.javacodegeeks.hughwphamill.mockito.hamcrest.matchers.IsEven.isEven;public class IsEvenTest {}

接下来,我们将编写一种测试方法来测试匹配器评估为true的肯定情况。

@Test
public void should_pass_for_even_number() throws Exception {// GivenInteger test = 4;// ThenassertThat(test, isEven());
}

还有一种显示匹配器无法匹配的输出的方法。

@Test
public void should_fail_for_odd_number() throws Exception {// GivenInteger test = 5;// ThenassertThat(test, isEven());
}

这将生成以下输出:

java.lang.AssertionError:
Expected: An Even numberbut: was <5>, which is an Odd number

通常,在为匹配器编写真实测试时,我们希望通过测试以确保逻辑是正确的,毕竟我们不希望在测试失败的项目上工作! 如果我们想这样做,我们可以将断言更改为以下内容:

assertThat(test, not(isEven()));

但是,在开发过程中编写失败的测试以手动检查匹配器的输出可能会很有用。

divisibleBy(整数除数)

现在我们已经看到了如何创建一个自定义Matcher来测试Integer的固有属性。 是奇数还是偶数? 但是,我们在hamcrest Matcher库中看到许多Matchers会根据输入值进行测试。 现在,我们将自己创建一个Matcher。

想象一下,我们想定期测试数字以发现它们是否可以被另一个数字整除。 我们可以编写一个自定义Matcher为我们完成此任务。

首先,像上一个示例一样,创建一个无参匹配器,并将除数硬编码为3。

public class DivisibleBy extends TypeSafeDiagnosingMatcher<Integer> {@Overrideprotected boolean matchesSafely(Integer integer, Description description) {int remainder = integer % 3; // Hardcoded to 3 for now!description.appendText("was ").appendValue(integer).appendText(" which left a remainder of ").appendValue(remainder);return remainder == 0;}@Overridepublic void describeTo(Description description) {description.appendText("A number divisible by 3"); // Hardcoded to 3 for now!}public static DivisibleBy divisibleBy() {return new DivisibleBy();}
}

我们的Matcher看起来很像我们的IsEven() Matcher,但是不匹配的描述稍微复杂一些。

我们如何从硬编码值更改为输入值? 没什么花招,实际上就像添加一个私有成员变量并将其设置在构造函数中,然后将值通过factory方法传递一样容易。

现在让我们看一下完整的Matcher:

public class DivisibleBy extends TypeSafeDiagnosingMatcher<Integer> {private final Integer divisor;public DivisibleBy(Integer divisor) {this.divisor = divisor;}@Overrideprotected boolean matchesSafely(Integer integer, Description description) {int remainder = integer % 3; // Hardcoded to 3 for now!description.appendText("was ").appendValue(integer).appendText(" which left a remainder of ").appendValue(remainder);return remainder == 0;}@Overridepublic void describeTo(Description description) {description.appendText("A number divisible by 3"); // Hardcoded to 3 for now!}public static DivisibleBy divisibleBy(Integer divisor) {return new DivisibleBy(divisor);}
}

再次,让我们创建一些测试来练习我们的新Matcher:

public class DivisibleByTest {@Testpublic void should_pass_for_true_divisor() throws Exception {// GivenInteger test = 15;// ThenassertThat(test, is(divisibleBy(5)));}@Testpublic void should_fail_for_non_divisor() throws Exception {// GivenInteger test = 17;// ThenassertThat(test, is(divisibleBy(3)));}
}

测试失败的输出是

java.lang.AssertionError:
Expected: is A number divisible by 3but: was <17> which left a remainder of <2>

现在我们已经看到了如何为现有类创建自定义匹配器,这些类可以测试内部属性或接受测试值。

接下来,我们将为自己编写的类创建自定义匹配器。

4.针对您自己的班级的自定义匹配器

当我们使用自己创建的类进行工作时,自定义Hamcrest匹配器是测试库中的强大工具。 现在,我们将创建一个域模型,并编写一些自定义匹配器以使用该模型。

我们的模型:一棵树

编程中常见的数据结构是由许多节点组成的树。 这些节点可能只有一个父节点或一个或多个子节点。 没有父节点的节点称为根。 没有子节点的节点称为叶子。 如果可以通过X的父级从X到Y跟踪路径,则将节点X视为另一个节点Y的后代。如果Y是X的后代,则将节点X视为另一个节点Y的祖先。如果X和Y都共享父节点,则视为另一个节点Y的兄弟。

我们将使用Node存储单个int值。

我们的模型将有一个名为Node的类,如下所示:

/**
* Node class for building trees
** Uses instance equality.
*/
public class Node {private final int value;private Node parent;private final Set<Node> children;/*** Create a new Node with the input value*/public Node(int value) {this.value = value;children = new HashSet<>();}/*** @return The value of this Node*/public int value() {return value;}/*** @return The parent of this Node*/public Node parent() {return parent;}/*** @return A copy of the Set of children of this Node*/public Set<Node> children() {return new HashSet<>(children);}/*** Add a child to this Node** @return this Node*/public Node add(Node child) {if (child != null) {children.add(child);child.parent = this;}return this;}/*** Remove a child from this Node** @return this Node*/public Node remove(Node child) {if (child != null && children.contains(child)) {children.remove(child);child.parent = null;}return this;}public String toString() {StringBuilder builder = new StringBuilder();builder.append("Node{").append("value=").append(value).append(",").append("parent=").append(parent != null ?parent.value : "null").append(",").append("children=").append("[").append(children.stream().map(n -> Integer.toString(n.value)).collect(Collectors.joining(","))).append("]}");return builder.toString();}
}

请注意,对于这个简单的示例,我们的类不是线程安全的。

该模型使我们能够构建一个节点树,从根节点开始并添加子节点。 我们可以在下面的小型应用程序类中看到这一点:

public class App {public static void main(String... args) {Node root = createTree();printNode(root);}private static Node createTree() {/*1/       \2           3/   \         /   \4       5   6       7/   \       |8      9   10*/Node root = new Node(1);root.add(new Node(2).add(new Node(4).add(new Node(8)).add(new Node(9))).add(new Node(5).add(new Node(10)))).add(new Node(3).add(new Node(6)).add(new Node(7)));return root;}private static void printNode(Node node) {System.out.println(node);for (Node child : node.children()) {printNode(child);}}
}

这将产生以下输出:

Node{value=1,parent=null,children=[3,2]}
Node{value=3,parent=1,children=[7,6]}
Node{value=7,parent=3,children=[]}
Node{value=6,parent=3,children=[]}
Node{value=2,parent=1,children=[5,4]}
Node{value=5,parent=2,children=[10]}
Node{value=10,parent=5,children=[]}
Node{value=4,parent=2,children=[8,9]}
Node{value=8,parent=4,children=[]}
Node{value=9,parent=4,children=[]}

现在我们已经定义了模型并知道如何使用它,我们可以开始针对它创建一些Matchers。

我们将在类中使用Node测试治具,以便针对一致的模型进行测试,该模型将与示例应用程序中定义的树结构相同。 夹具在这里定义:

public class NodeTestFixture {static Node one = new Node(1);static Node two = new Node(2);static Node three = new Node(3);static Node four = new Node(4);static Node five = new Node(5);static Node six = new Node(6);static Node seven = new Node(7);static Node eight = new Node(8);static Node nine = new Node(9);static Node ten = new Node(10);static {one.add(two);one.add(three);two.add(four);two.add(five);three.add(six);three.add(seven);four.add(eight);four.add(nine);five.add(ten);}
}

叶()

我们将创建的第一个Matcher将检查输入节点是否为叶节点。 它将通过检查输入节点是否有任何子节点来完成此操作,如果有则子节点不是叶节点。

public class IsLeaf extends TypeSafeDiagnosingMatcher<Node> {@Overrideprotected boolean matchesSafely(Node node, Description mismatchDescription) {if (!node.children().isEmpty()) {mismatchDescription.appendText("a node with ").appendValue(node.children().size()).appendText(" children");return false;}return true;}@Overridepublic void describeTo(Description description) {description.appendText("a leaf node with no children");}public static IsLeaf leaf() {return new IsLeaf();}
}

测试类别:

public class IsLeafTest extends NodeTestFixture {@Testpublic void should_pass_for_leaf_node() throws Exception {// GivenNode node = NodeTestFixture.seven;// ThenassertThat(node, is(leaf()));}@Testpublic void should_fail_for_non_leaf_node() throws Exception {// GivenNode node = NodeTestFixture.four;// ThenassertThat(node, is(not(leaf())));}
}

根()

现在,我们将创建一个匹配器,以检查节点是否为根节点。 我们将通过检查父节点的存在来做到这一点。

public class IsRoot extends TypeSafeDiagnosingMatcher<Node> {@Overrideprotected boolean matchesSafely(Node node, Description mismatchDescription) {if (node.parent() != null) {mismatchDescription.appendText("a node with parent ").appendValue(node.parent());return false;}return true;}@Overridepublic void describeTo(Description description) {description.appendText("a root node with no parent");}public static IsRoot root() {return new IsRoot();}
}

测试类别:

public class IsRootTest {@Testpublic void should_pass_for_root_node() throws Exception {// GivenNode node = NodeTestFixture.one;// ThenassertThat(node, is(root()));}@Testpublic void should_fail_for_non_root_node() throws Exception {// GivenNode node = NodeTestFixture.five;// ThenassertThat(node, is(not(root())));}
}

DescendantOf(节点节点)

接下来是带有输入的匹配器,我们将检查给定Node是否为输入Node的后代。 我们将向上移动父母,看看是否在根之前到达了测试节点。

public class IsDescendant extends TypeSafeDiagnosingMatcher<Node> {private final Node ancestor;public IsDescendant(Node ancestor) {this.ancestor = ancestor;}@Overrideprotected boolean matchesSafely(Node node, Description description) {while (node.parent() != null) {if (node.parent().equals(ancestor)) {return true;}node = node.parent();}description.appendText("a Node which was not a descendant of ").appendValue(ancestor);return false;}@Overridepublic void describeTo(Description description) {description.appendText("a descendant Node of ").appendValue(ancestor);}public static IsDescendant descendantOf(Node ancestor) {return new IsDescendant(ancestor);}
}

测试类别:

public class IsDescendantTest {@Testpublic void should_pass_for_descendant_node() throws Exception {// GivenNode node = NodeTestFixture.nine;Node ancestor = NodeTestFixture.two;// ThenassertThat(node, is(descendantOf(ancestor)));}@Testpublic void should_fail_for_non_descendant_node() throws Exception {// GivenNode node = NodeTestFixture.ten;Node ancestor = NodeTestFixture.three;// ThenassertThat(node, is(not(descendantOf(ancestor))));}
}

ancestorOf(节点节点)

下一步将检查给定的节点是否是输入节点的祖先。 此操作实际上是descendantOf()的相反操作,因此我们将上移输入节点的父级而不是测试节点。

public class IsAncestor extends TypeSafeDiagnosingMatcher<Node> {private final Node descendant;public IsAncestor(Node descendant) {this.descendant = descendant;}@Overrideprotected boolean matchesSafely(Node node, Description description) {Node descendantCopy = descendant;while (descendantCopy.parent() != null) {if (descendantCopy.parent().equals(node)) {return true;}descendantCopy = descendantCopy.parent();}description.appendText("a Node which was not an ancestor of ").appendValue(descendant);return false;}@Overridepublic void describeTo(Description description) {description.appendText("an ancestor Node of ").appendValue(descendant);}public static IsAncestor ancestorOf(Node descendant) {return new IsAncestor(descendant);}
}

测试类别:

public class IsAncestorTest {@Testpublic void should_pass_for_ancestor_node() throws Exception {// GivenNode node = NodeTestFixture.two;Node descendant = NodeTestFixture.ten;// ThenassertThat(node, is(ancestorOf(descendant)));}@Testpublic void should_fail_for_non_ancestor_node() throws Exception {// GivenNode node = NodeTestFixture.three;Node descendant = NodeTestFixture.eight;// ThenassertThat(node, is(not(ancestorOf(descendant))));}
}

siblingOf(节点节点)

最后,我们将创建一个Matcher来检查输入节点是否是另一个节点的同级节点。 我们将检查他们是否共享父母。 此外,当用户尝试测试根节点的同级节点时,我们将进行一些检查并提供一些输出。

public class IsSibling extends TypeSafeDiagnosingMatcher<Node> {private final Node sibling;public IsSibling(Node sibling) {this.sibling = sibling;}@Overrideprotected boolean matchesSafely(Node node, Description description) {if (sibling.parent() == null) {description.appendText("input root node cannot be tested for siblings");return false;}if (node.parent() != null && node.parent().equals(sibling.parent())) {return true;}if (node.parent() == null) {description.appendText("a root node with no siblings");}else {description.appendText("a node with parent ").appendValue(node.parent());}return false;}@Overridepublic void describeTo(Description description) {if (sibling.parent() == null) {description.appendText("a sibling of a root node");} else {description.appendText("a node with parent ").appendValue(sibling.parent());}}public static IsSibling siblingOf(Node sibling) {return new IsSibling(sibling);}
}

测试类别:

public class IsSiblingTest {@Testpublic void should_pass_for_sibling_node() throws Exception {// GivenNode a = NodeTestFixture.four;Node b = NodeTestFixture.five;// ThenassertThat(a, is(siblingOf(b)));}@Testpublic void should_fail_for_testing_root_node() throws Exception {// GivenNode a = NodeTestFixture.one;Node b = NodeTestFixture.six;// ThenassertThat(a, is(not(siblingOf(b))));}@Testpublic void should_fail_for_input_root_node() throws Exception {// GivenNode a = NodeTestFixture.five;Node b = NodeTestFixture.one;// ThenassertThat(a, is(not(siblingOf(b))));}@Testpublic void should_fail_for_non_sibling_node() throws Exception {// GivenNode a = NodeTestFixture.five;Node b = NodeTestFixture.six;// ThenassertThat(a, is(not(siblingOf(b))));}
}

5.结论

现在我们已经看到了如何创建Custom Hamcrest Matchers来测试既有的标准Java类和我们自己的类。 在下一个教程中,我们将把到目前为止所学到的所有知识放在一起,因为我们了解了将模拟和测试放在首位的软件开发技术。 测试驱动开发。

翻译自: https://www.javacodegeeks.com/2015/11/custom-hamcrest-matchers.html

matchers依赖

matchers依赖_定制Hamcrest Matchers相关推荐

  1. 定制Hamcrest Matchers

    本文是我们名为" 用Mockito进行测试 "的学院课程的一部分. 在本课程中,您将深入了解Mockito的魔力. 您将了解有关"模拟","间谍&qu ...

  2. matchers依赖_Hamcrest Matchers教程

    matchers依赖 本文是我们名为" 用Mockito测试 "的学院课程的一部分. 在本课程中,您将深入了解Mockito的魔力. 您将了解有关"模拟",&q ...

  3. Hamcrest Matchers教程

    本文是我们名为" 用Mockito进行测试 "的学院课程的一部分. 在本课程中,您将深入了解Mockito的魔力. 您将了解有关"模拟","间谍&qu ...

  4. matchers依赖_Hamcrest Matchers的高级创建

    matchers依赖 介绍 上一次 ,我讨论了Hamcrest Matcher是什么,如何使用以及如何制作. 在本文中,我将解释创建Hamcrest Matchers的更多高级步骤. 首先,我将分享如 ...

  5. matchers依赖_Hamcrest Matchers,Guava谓词和Builder设计模式

    matchers依赖 通常,在编码时,我们必须处理其中包含数十个字段的一些POJO对象. 很多时候,我们通过一个带有数十个参数的构造函数来初始化这些类,这以任何可能的想象的方式都是可怕的. 除此之外, ...

  6. Hamcrest Matchers的高级创建

    介绍 上一次 ,我讨论了Hamcrest Matcher是什么,如何使用以及如何制作. 在本文中,我将解释创建Hamcrest Matchers的更多高级步骤. 首先,我将分享如何使您的匹配器更易于类 ...

  7. Hamcrest Matchers,Guava谓词和Builder设计模式

    通常,在编码时,我们必须处理其中包含数十个字段的一些POJO对象. 很多时候,我们通过一个带有数十个参数的构造函数来初始化这些类,这以任何可能的想象的方式都是可怕的. 除此之外,使用这些构造函数的函数 ...

  8. 导包:org.hamcrest.Matchers.equalTo报错解决方法

    今天在学习SpringBoot写测试的时候,看网上案例导包org.hamcrest.Matchers.equalTo,一模一样啊,但我就是报错,我猜想肯定是我版本问题,是不是新版本的equalTo方法 ...

  9. maven插件依赖_当Maven依赖插件位于

    maven插件依赖 问题: 我们进行了一个集成测试,该测试创建了一个Spring ClassPathXmlApplicationContext ,同时这样做导致NoSuchMethodError爆炸. ...

最新文章

  1. Android开发之通过Handler的post方法更新UI
  2. Spring Boot 2.x基础教程:使用 Thymeleaf开发Web页面
  3. nanopi 创建共享文件夹
  4. Python __dict__和vars()
  5. 计算机文化基础主要讲了什么,计算机文化基础—讲义
  6. GC解释:收集器概述
  7. ANTLR入门:构建一种简单的表达语言
  8. windows下如何在命令行里切换到任意目录
  9. 网络中的最基本的服务器DNS的相关知识的介绍
  10. C/C++ 获取目录下的文件列表信息
  11. android 横向竖排文字,GitHub - tung666/AdvancedTextView: 一个增强的TextView库。可以实现文字两端对齐,文字竖排,以及自定义选择文字后的弹出菜单。...
  12. nginx php多域名配置文件,配置文件,nginx_nginx多站点配置,无法通过域名访问,使用ip访问会跳转到其中一个站点,配置文件,nginx - phpStudy...
  13. Apache Doris1.0版本集群搭建、负载均衡与参数调优
  14. 爪哇国新游记之七----使用ArrayList统计水果出现次数
  15. Hadoop系列之-7、Hadoop3.x的介绍
  16. happybase对hbase数据库的基本操作
  17. swap()函数实现变量值的交换
  18. [xml]DOM4j解析
  19. 外企面试英文自我介绍【面试经验】
  20. P2P对等网络技术原理整合

热门文章

  1. 2021牛客OI赛前集训营-提高组(第五场)D-牛牛的border【SAM】
  2. Bzoj3309-DZY Loves Math【莫比乌斯反演,线性筛】
  3. jzoj3918-蛋糕【二分】
  4. jzoj6274-[NOIP提高组模拟1]梦境【贪心,堆】
  5. codeforces1456 D. XOR-gun
  6. 13、play中实现信息国际化
  7. 类和对象运行时在内存里是怎么样的?各种变量、方法在运行时是怎么交互的?
  8. Java架构师必看的10本书
  9. laravel允许所有网站进行跨域操作
  10. 消费端整合SpringCloudGateway