SwiftHamcrest 2.2.4

SwiftHamcrest 2.2.4

测试已测试
语言语言 SwiftSwift
许可证 NOASSERTION
发布最后发布2023年3月
SPM支持 SPM

Nikolaj SchumacherRené Pirringer 维护。



  • Nikolaj Schumacher 和 René Pirringer

Swift Hamcrest

构建状态](https://github.com/nschum/SwiftHamcrest/actions/workflows/build.yml/badge.svg) Swift 5.7 OS X ≥ 11.0 iOS ≥ 11.0 Carthage 兼容](https://github.com/Carthage/Carthage) CocoaPods 兼容

Hamcrest 为您的 Swift 单元测试提供了高级匹配器和更好的错误消息。

Hamcrest 最初是用 Java 编写的,适用于许多 语言

教程

此教程还可在 Hamcrest 的工作区中以游乐场的形式获取。

通常,您在单元测试中使用这些匹配器,其中不匹配会导致测试失败,但它们也可以在游乐场中使用,其中不匹配会简单地打印出错误消息。

在任一情况下,都需要导入 Hamcrest 模块。

import Hamcrest

操作匹配器

以下是一些非常简单的匹配器。匹配到的表达式看起来像常规布尔表达式,但提供可读的匹配错误消息,而不是通用错误。

let x = 1 + 1

// The comments show the human-readable error messages created by the assertions.

assertThat(x == 2) //
assertThat(x == 3) // GOT: 2, EXPECTED: equal to 3

assertThat(x > 1) //
assertThat(x > 2) // GOT: 2, EXPECTED: greater than 2

assertThat(x >= 2) //
assertThat(x >= 3) // GOT: 2, EXPECTED: greater than or equal to 3

assertThat(x < 3) //
assertThat(x < 2) // GOT: 2, EXPECTED: less than 2

assertThat(x <= 2) //
assertThat(x <= 1) // GOT: 2, EXPECTED: less than or equal to 1

class Test {}
let o = Test()
assertThat(o === o) //
assertThat(o === Test())
// GOT: __lldb_expr_8.Test (0x7f9572b020d0),
// EXPECTED: same instance as 0x7f9570c702a0

文本匹配器

所有这些匹配器也都可以作为函数使用。

assertThat(x, equalTo(2)) //
assertThat(x, equalTo(3)) // GOT: 2, EXPECTED: equal to 3

assertThat(x, greaterThan(1)) //
assertThat(x, greaterThan(2)) // GOT: 2, EXPECTED: greater than 2

assertThat(x, greaterThanOrEqualTo(2)) //
assertThat(x, greaterThanOrEqualTo(3))
// GOT: 2, EXPECTED: greater than or equal to 3

assertThat(x, lessThan(3)) //
assertThat(x, lessThan(2)) // GOT: 2, EXPECTED: less than 2

assertThat(x, lessThanOrEqualTo(2)) //
assertThat(x, lessThanOrEqualTo(1))
// GOT: 2, EXPECTED: less than or equal to 1

assertThat(x, inInterval(1...2)) //
assertThat(x, inInterval(1..<2)) // GOT: 2, EXPECTED: in interval 1..<2

assertThat(o, sameInstance(o)) //
assertThat(o, sameInstance(Test()))
// GOT: __lldb_expr_53.Test, EXPECTED: same instance as __lldb_expr_53.Test

这里有一些更直接的匹配器

assertThat("foobarbaz", containsString("bar")) //
assertThat("foobarbaz", containsString("bla"))
// GOT: "foobarbaz", EXPECTED: contains "bla"

assertThat("foobarbaz", containsStringsInOrder("f", "b", "b")) //
assertThat("foobarbaz", containsStringsInOrder("foo", "baz", "bar"))
// GOT: "foobarbaz", EXPECTED: contains in order ["foo", "baz", "bar"]

assertThat("foobarbaz", hasPrefix("foo")) //
assertThat("foobarbaz", hasPrefix("oo"))
// GOT: "foobarbaz", EXPECTED: has prefix "oo"

assertThat("foobarbaz", hasSuffix("baz")) //
assertThat("foobarbaz", hasSuffix("ba"))
// GOT: "foobarbaz", EXPECTED: has suffix "ba"

assertThat("ac", matchesPattern("\\b(a|b)(c|d)\\b")) //
assertThat("BD", matchesPattern("\\b(a|b)(c|d)\\b", options: .caseInsensitive)) //
assertThat("aC", matchesPattern("\\b(a|b)(c|d)\\b"))
// "GOT: "aC", EXPECTED: matches \b(a|b)(c|d)\b"

assertThat(10.0, closeTo(10.0, 0.01)) //
assertThat(10.0000001, closeTo(10, 0.01)) //
assertThat(10.1, closeTo(10, 0.01))
// GOT: 10.1 (difference of 0.0999999999999996), EXPECTED: within 0.01 of 10.0

import Foundation
assertThat(CGPoint(x: 5, y: 10), hasProperty("x", closeTo(5.0, 0.00001))) //
assertThat(CGPoint(x: 5, y: 10), hasProperty("y", closeTo(0.0, 0.00001)))
// GOT: (5.0,10.0) (property value 10.0 (difference of 10.0)),
// EXPECTED: has property "y" with value within 1e-05 of 0.0

组合匹配器

Hamcrest的真正实力在于将多个匹配器组合成一个断言语句。

assertThat(x, not(equalTo(3))) //
assertThat(x, not(equalTo(2))) // GOT: 2, EXPECTED: not equal to 2

assertThat(x, allOf(greaterThan(1), lessThan(3))) //
assertThat(x, allOf(greaterThan(2), lessThan(3)))
// GOT: 2 (mismatch: greater than 2),
// EXPECTED: all of [greater than 2, greater than 3]

assertThat(x, greaterThan(1) && lessThan(3)) //
assertThat(x, greaterThan(2) && lessThan(3))
// GOT: 2 (mismatch: greater than 2),
// EXPECTED: all of [greater than 2, greater than 3]

assertThat(x, anyOf(greaterThan(2), lessThan(3))) //
assertThat(x, anyOf(greaterThan(2), lessThan(2)))
// GOT: 2, EXPECTED: any of [greater than 2, greater than 2]

assertThat(x, greaterThan(2) || lessThan(3)) //
assertThat(x, greaterThan(2) || lessThan(2))
// GOT: 2, EXPECTED: any of [greater than 2, greater than 2]

集合

组合匹配器对于匹配序列和字典尤其有用。

let array = ["foo", "bar"]

assertThat(array, hasCount(2)) //
assertThat(array, hasCount(greaterThan(2)))
// GOT: [foo, bar] (count 2), EXPECTED: has count greater than 2

assertThat(array, everyItem(equalTo("foo")))
// GOT: [foo, bar] (mismatch: bar),
// EXPECTED: a sequence where every item equal to foo

assertThat(array, contains("foo", "bar")) //
assertThat(array, contains(equalTo("foo"), equalTo("bar"))) //
assertThat(array, contains(equalTo("foo")))
// GOT: [foo, bar] (unmatched item "bar"),
// EXPECTED: a sequence containing equal to foo
assertThat(array, contains(equalTo("foo"), equalTo("baz")))
// "GOT: [foo, bar] (mismatch: GOT: "bar", EXPECTED: equal to baz),
// EXPECTED: a sequence containing [equal to foo, equal to baz]"
assertThat(array, contains(equalTo("foo"), equalTo("bar"), equalTo("baz")))
// GOT: [foo, bar] (missing item equal to baz),
// EXPECTED: a sequence containing [equal to foo, equal to bar, equal to baz]

assertThat(array, containsInAnyOrder("bar", "foo")) //
assertThat(array, containsInAnyOrder(equalTo("bar"), equalTo("foo"))) //

assertThat(array, hasItem(equalTo("foo"))) //
assertThat(array, hasItem(equalTo("baz")))
// GOT: [foo, bar], EXPECTED: a sequence containing equal to baz

assertThat(array, hasItem("foo", atIndex: 0))) //
assertThat(array, hasItem("foo", atIndex: 1))) // GOT: ["foo", "bar"], EXPECTED: a sequence containing "foo" at index 1"

assertThat(array, hasItem(equalTo("foo"), atIndex: 0))) //
assertThat(array, hasItem(equalTo("foo"), atIndex: 1))) // GOT: ["foo", "bar"], EXPECTED: a sequence containing "foo" at index 1"


assertThat(array, hasItems("foo", "bar")) //
assertThat(array, hasItems(equalTo("foo"), equalTo("baz")))
// GOT: [foo, bar] (missing item equal to baz),
// EXPECTED: a sequence containing all of [equal to foo, equal to baz]
let dictionary = ["foo": 5, "bar": 10]

assertThat(dictionary, hasEntry("foo", 5)) //
assertThat(dictionary, hasEntry(equalTo("foo"), equalTo(5))) //
assertThat(dictionary, hasEntry(equalTo("foo"), equalTo(10)))
// GOT: [bar: 10, foo: 5],
// EXPECTED: a dictionary containing [equal to foo -> equal to 10]

assertThat(dictionary, hasKey("foo")) //
assertThat(dictionary, hasKey(equalTo("baz")))
// GOT: [bar: 10, foo: 5],
// EXPECTED: a dictionary containing [equal to baz -> anything]

assertThat(dictionary, hasValue(10)) //
assertThat(dictionary, hasValue(equalTo(15)))
// GOT: [bar: 10, foo: 5],
// EXPECTED: a dictionary containing [anything -> equal to 15]

可选类型

匹配器不需要匹配Swift中偏好非可选类型的特性。presentAnd可以将匹配器明确地应用到可选类型上。

var optional: Int = 1 + 1

assertThat(optional, present()) //
assertThat(optional, nilValue()) // GOT: Optional(2), EXPECTED: nil

assertThat(optional, presentAnd(equalTo(2))) //
assertThat(optional, presentAnd(equalTo(1)))
// GOT: Optional(2), EXPECTED: present and equal to 1

数据类型和转置

以下匹配器可以用于断言数据类型。类型为 Any 的引用在使用类型匹配器之前需要先进行转置。可以使用 instanceOf(and:) 结合类型验证和转置。

class TestChild: Test {}
assertThat(o, instanceOf(Test.self)) //
assertThat(o, instanceOf(TestChild.self))
// GOT: __lldb_expr_60.Test, EXPECTED: instance of expected type

let any: Any = 10
assertThat(any, instanceOf(Int.self, and: equalTo(10))) //
assertThat(any, instanceOf(Double.self, and: equalTo(10.0)))
// GOT: 10 (mismatched type), EXPECTED: instance of and equal to 10.0
assertThat(any, instanceOf(Int.self, and: equalTo(5)))
// GOT: 10, EXPECTED: instance of and equal to 5

自定义匹配器

创建自定义匹配器有两种方式。第一种方式是创建一个仅返回现有匹配器组合的函数。

func isOnAxis<Point>() -> Matcher<Point> {
    return anyOf(hasProperty("x", closeTo(0.0, 0.00001)),
                 hasProperty("y", closeTo(0.0, 0.00001)))
}

assertThat(CGPoint(x: 0, y: 10), isOnAxis()) //
assertThat(CGPoint(x: 5, y: 10), isOnAxis())
// GOT: (5.0,10.0),
// EXPECTED: any of [has property "x" with value within 1e-05
// of 0.0, has property "y" with value within 1e-05 of 0.0]

可以使用描述为特殊匹配器来自定义描述。

func isOnAxis2<Point>() -> Matcher<Point> {
    return describedAs("a point on an axis",
        anyOf(hasProperty("x", closeTo(0.0, 0.00001)),
              hasProperty("y", closeTo(0.0, 0.00001))))
}

assertThat(CGPoint(x: 0, y: 10), isOnAxis2()) //
assertThat(CGPoint(x: 5, y: 10), isOnAxis2())
// GOT: (5.0,10.0), EXPECTED: a point on an axis

第二种方式是从头开始创建匹配器。SwiftHamcrest特别关注使此类自定义匹配器易于编写。在许多Hamcrest实现中,通常创建一个类。在SwiftHamcrest中,只需创建一个带有自定义闭包的Matcher实例,该闭包接受一个值并返回一个布尔值。

func isEven() -> Matcher<Int> {
    return Matcher("even") {$0 % 2 == 0}
}

assertThat(x, isEven()) //
assertThat(3, isEven()) // GOT: 3, EXPECTED: even

虽然布尔值很方便(并且在大多数情况下足够),但在某些情况下,你可能希望获取更多关于误匹配的信息。除了布尔值之外,你也可以让闭包返回一个 MatchResult 枚举。如果误匹配不明显,这一点尤其有用。

func isDivisibleByThree() -> Matcher<Int> {
    return Matcher("divisible by three") {
        (value) -> MatchResult in
        if value % 3 == 0 {
            return .Match
        } else {
            return .Mismatch("remainder: \(value % 3)")
        }
    }
}

assertThat(342783, isDivisibleByThree()) //
assertThat(489359, isDivisibleByThree())
// GOT: 489359 (remainder: 2), EXPECTED: divisible by three

错误

如果正在测试的函数可以抛出错误,Hamcrest将会报告这些错误。

private enum SampleError: Error {
    case Error1
    case Error2
}

private func throwingFunc() throws -> Int {
    throw SampleError.Error1
}

assertThat(try throwingFunc(), equalTo(1)) // ERROR: SampleError.Error1

如果不想测试可能抛出错误的函数的结果,或者如果该函数不返回任何错误,请使用 assertNotThrows

private func notThrowingFunc() throws {
}

assertNotThrows(try notThrowingFunc()) //
assertNotThrows(_ = try throwingFunc()) // ERROR: UNEXPECTED ERROR

如果想要验证是否抛出了错误,请使用 assertThrows

assertThrows(try notThrowingFunc()) // EXPECTED ERROR
assertThrows(try notThrowingFunc(), SampleError.Error2)
// EXPECTED ERROR: SampleError.Error2

assertThrows(try throwingFunc(), SampleError.Error2)
// GOT ERROR: SampleError.Error1, EXPECTED ERROR: SampleError.Error2

消息

如果匹配器没有给出有意义的消息,您可以为匹配器未匹配时显示的自定义消息添加。

assertThat(true, equalTo(false), message: "Custom error message")

// 失败:自定义错误消息 – 实际得到的:等于 true,期望:等于 false

集成

Swift 包管理器

在您的 Xcode 项目中选择'添加包依赖项'选项,并在'选择包仓库'窗口中复制此仓库的 URL。

CocoaPods

使用类似以下 Podfile 集成 SwiftHamcrest

use_frameworks!

target 'HamcrestDemoTests' do
  inherit! :search_paths
  pod 'SwiftHamcrest', '~> 2.2.4'
end

Carthage

在您的 Cartfile 中添加以下内容

github "nschum/SwiftHamcrest"