本文是我们名为“ 用Mockito进行测试 ”的学院课程的一部分。
在本课程中,您将深入了解Mockito的魔力。 您将了解有关“模拟”,“间谍”和“部分模拟”的信息,以及它们相应的Stubbing行为。 您还将看到使用测试双打和对象匹配器进行验证的过程。 最后,讨论了使用Mockito的测试驱动开发(TDD),以了解该库如何适合TDD的概念。 在这里查看 !
在本教程中,我们将研究Hamcrest Matcher库以及如何将其与JUnit和Mockito集成。
1.什么是Hamcrest?
Hamcrest是用于创建匹配对象的框架。 这些匹配器对象是谓词,用于编写在某些条件下可以满足的规则。 它们通常用于自动化测试中,尽管它们可以用于其他情况下,例如数据验证。 Hamcrest让我们超越了简单的JUnit断言,并使我们能够编写非常具体,可读的验证代码。
Hamcrest旨在使测试更具可读性。 它充分利用静态方法来创建断言语法,该语法易于编写和理解。 当与JUnit和Mockito结合使用时,它使我们能够编写清晰,简洁的测试,以满足“测试一件事”的良好单元测试的特性。
假设我们有一个String,我们想测试它是否与另一个期望的字符串相等,我们可以使用JUnit断言来测试它:
assertEquals(expected, actual);
在Hamcrest中,我们使用JUnit assertThat(valueUnderTest, matcher)
方法。 此方法始终构成Hamcrest断言的基础; 我们断言被测值满足匹配谓词。 要在最基本的水平上用hamcrest重写上述测试,我们可以编写:
assertThat(actual, equalTo(expected));
注意assertThat约定将被测的实际值作为第一个参数,这与assertEquals约定相反。 这是对可读性的改进,但是Hamcrest还以is()
匹配器的形式为我们提供了一些不错的语法糖。 该匹配器本身不执行任何操作,它只是中继其输入匹配器的结果,从而使您的断言代码可以像英语一样读取。 让我们使用is()
重写上面的内容:
assertThat(actual, is(equalTo(expected)));
很好,很可读!
Hamcrest的匹配器失败时,它会生成详细的输出,并指定期望值和实际值,以帮助您确定测试失败的原因。 查看以下测试用例:
@Test
public void test_failed() throws Exception {
// Given
Integer number = 7;
// Then
assertThat(number, greaterThan(10));
}
显然,该测试将失败,但是Hamcrest将提供有关失败的详细信息:
java.lang.AssertionError:
Expected: a value greater than <10>
but: <7> was less than <10>
在本教程中,我们将遵循仅在一个单元测试中包含一个断言的约定。 这似乎是重复的,尤其是在许多测试中测试的设置部分相同的情况下,但是这是单元测试中的良好做法。 它使我们能够创建针对性的测试-仅当单个断言失败时,测试才会失败,其他所有断言将继续执行。 它使我们能够创建可读的测试-我们可以一目了然地看到测试的目的。 它使我们能够创建记录代码的测试-我们可以使用表达测试目的的测试名称,从而传达测试的详细目的(请考虑customer_should_have_balance_updated_by_input_order_amount()
而不是verifyOrderMethod()
)。 它使我们能够创建不易碎的测试-如果测试做了太多,如果更改了不相关的功能,它可能会中断,从而迫使我们重写测试只是为了使其再次正常运行而无需更改实际的被测代码。
如果我们遵循“测试一件事”的习惯,那么将来我们将编写出更好的单元测试!
2.包括Hamcrest
如果使用的是Maven,则可以将Hamcrest添加到您的项目中,并且对pom.xml具有以下依赖性
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
</dependency>
如果您使用的是Gradle,请添加以下内容
dependencies {
testCompile "org.hamcrest:hamcrest-all:1.3"
}
要将hamcrest直接添加到项目的类路径中,可以从https://code.google.com/p/hamcrest/下载hamcrest-all-1.3.jar到硬盘上的某个位置。
右键单击您的eclipse项目,然后选择“属性”,然后在左窗格中选择“ Java Build Path”,然后在右侧选择“ Libraries”。
在“库”选项卡上,单击“添加外部罐子”按钮,然后导航到先前下载的所有hamcrest罐子。 选择罐子,它现在已添加到您的项目中并可供使用。
请注意,JUnit与简化版本的Hamcrest(Hamcrest Core)捆绑在一起,因此,如果JUnit出现在类路径上的Hamcrest之前,则编译器将选择该版本。 为了解决这个问题,请确保Hamcrest在类路径上的JUnit之前出现。 您可以在Maven中通过在所有其他依赖项之前列出hamcrest-all依赖项来实现此目的。
与Mockito静态方法一样,我们可以通过启动Window-> Preferences并将Hamcrest库添加到Eclipse内容辅助中,并转到左侧导航栏中的Java / Editor / Content Assist / Favorites。 之后,按照图1添加以下内容作为“ New Type…”
- org.hamcrest.Matchers
启动Window-> Preferences,然后转到左侧导航栏中的Java / Editor / Content Assist / Favorites。 之后,按照图1添加以下内容作为“ New Type…”
3.认识匹配者
Hamcrest提供了一个静态工厂方法库,用于在org.hamcrest.Matchers类中创建Matchers,因此您可以通过静态导入引入所有Matchers
import static org.hamcrest.Matchers.*
但是,如果这样做,则存在命名冲突的风险,因为Hamcrest和Mockito都提供了静态的any()
方法,因此建议导入您单独使用的每个静态方法。
现在,我们将在Hamcrest Matchers库中查看所有可用的Matchers。 它们将分为两大类: 用于测试值的匹配器(简单),和用于组合其他匹配器(聚合)的匹配器。
简单匹配器
以下匹配器主要用于测试输入值。
3.1.1。 任何()
匹配给定类型的任何变量。
@Test
public void test_any() throws Exception {
// Given
String myString = "hello";
// Then
assertThat(myString, is(any(String.class)));
}
3.1.2。 任何东西()
匹配任何东西。
@Test
public void test_anything() throws Exception {
// Given
String myString = "hello";
Integer four = 4;
// Then
assertThat(myString, is(anything()));
assertThat(four, is(anything()));
}
3.1.3。 arrayContaining()
数组的各种匹配器,数组的长度必须匹配匹配器的数量,并且它们的顺序很重要。
数组是否按输入到匹配器的顺序包含所有给定项目?
@Test
public void test_arrayContaining_items() throws Exception {
// Given
String[] strings = {"why", "hello", "there"};
// Then
assertThat(strings, is(arrayContaining("why", "hello", "there")));
}
数组中是否包含按顺序匹配匹配器输入列表的项目?
@Test
public void test_arrayContaining_list_of_matchers() throws Exception {
// Given
String[] strings = {"why", "hello", "there"};
// Then
java.util.List<org.hamcrest.Matcher<? super String>> itemMatchers = new ArrayList<>();
itemMatchers.add(equalTo("why"));
itemMatchers.add(equalTo("hello"));
itemMatchers.add(endsWith("here"));
assertThat(strings, is(arrayContaining(itemMatchers)));
}
数组是否按顺序包含与输入vararg匹配器匹配的项目?
@Test
public void test_arrayContaining_matchers() throws Exception {
// Given
String[] strings = {"why", "hello", "there"};
// Then
assertThat(strings, is(arrayContaining(startsWith("wh"), equalTo("hello"), endsWith("here"))));
}
3.1.4。 arrayContainingInAnyOrder()
数组的各种匹配器,数组的长度必须匹配匹配器的数量,但是它们的顺序并不重要。
数组是否包含所有给定项?
@Test
public void test_arrayContainingInAnyOrder_items() throws Exception {
// Given
String[] strings = { "why", "hello", "there" };
// Then
assertThat(strings, is(arrayContainingInAnyOrder(