使用 Nimble 来表达 Swift 或 Objective-C 表达式的预期结果。灵感来自 Cedar。
// Swift
expect(1 + 1).to(equal(2))
expect(1.2).to(beCloseTo(1.1, within: 0.1))
expect(3) > 2
expect("seahorse").to(contain("sea"))
expect(["Atlantic", "Pacific"]).toNot(contain("Mississippi"))
expect(ocean.isClean).toEventually(beTruthy())
目录 由 DocToc 生成
Apple 的 Xcode 包含了 XCTest 框架,它提供断言宏来测试代码是否表现良好。例如,为了断言 1 + 1 = 2
,XCTest 要求你编写
// Swift
XCTAssertEqual(1 + 1, 2, "expected one plus one to equal two")
或者在 Objective-C 中
// Objective-C
XCTAssertEqual(1 + 1, 2, @"expected one plus one to equal two");
XCTest 断言有几个缺点
Nimble 解决了这些问题。
expect(...).to
表达期望Nimble 允许您使用自然、易于理解的语言表达预期。
// Swift
import Nimble
expect(seagull.squawk).to(equal("Squee!"))
// Objective-C
@import Nimble;
expect(seagull.squawk).to(equal(@"Squee!"));
expect
函数自动补全包括file:
和line:
参数,但这些参数是可选的。使用默认值可以让 Xcode 在未满足预期时突出显示正确的行。
要执行相反的预期——断言某些内容不等于其他内容——请使用 toNot
或 notTo
。
// Swift
import Nimble
expect(seagull.squawk).toNot(equal("Oh, hello there!"))
expect(seagull.squawk).notTo(equal("Oh, hello there!"))
// Objective-C
@import Nimble;
expect(seagull.squawk).toNot(equal(@"Oh, hello there!"));
expect(seagull.squawk).notTo(equal(@"Oh, hello there!"));
你希望向测试的失败信息中添加更多信息吗?请使用description
可选参数来添加自己的文本
// Swift
expect(1 + 1).to(equal(3))
// failed - expected to equal <3>, got <2>
expect(1 + 1).to(equal(3), description: "Make sure libKindergartenMath is loaded")
// failed - Make sure libKindergartenMath is loaded
// expected to equal <3>, got <2>
或者Objective-C中的*WithDescription版本
// Objective-C
@import Nimble;
expect(@(1+1)).to(equal(@3));
// failed - expected to equal <3.0000>, got <2.0000>
expect(@(1+1)).toWithDescription(equal(@3), @"Make sure libKindergartenMath is loaded");
// failed - Make sure libKindergartenMath is loaded
// expected to equal <3.0000>, got <2.0000>
Nimble确保你不比较不匹配的两个类型
// Swift
// Does not compile:
expect(1 + 1).to(equal("Squee!"))
Nimble使用泛型(仅在Swift中可用)来确保类型正确。这意味着在Objective-C中使用Nimble时,类型检查不可用。
😭
打字太多烦了吗?在Nimble中,你可以使用重载运算符,如==
表示等价,或>
表示比较
// Swift
// Passes if squawk does not equal "Hi!":
expect(seagull.squawk) != "Hi!"
// Passes if 10 is greater than 2:
expect(10) > 2
运算符重载仅在Swift中可用,因此你无法在Objective-C中使用此语法。
💔
期待函数不会评估给它的值,直到需要匹配。因此,Nimble可以测试一个表达式在计算后是否抛出异常。
// Swift
// Note: Swift currently doesn't have exceptions.
// Only Objective-C code can raise exceptions
// that Nimble will catch.
// (see https://github.com/Quick/Nimble/issues/220#issuecomment-172667064)
let exception = NSException(
name: NSInternalInconsistencyException,
reason: "Not enough fish in the sea.",
userInfo: ["something": "is fishy"])
expect { exception.raise() }.to(raiseException())
// Also, you can customize raiseException to be more specific
expect { exception.raise() }.to(raiseException(named: NSInternalInconsistencyException))
expect { exception.raise() }.to(raiseException(
named: NSInternalInconsistencyException,
reason: "Not enough fish in the sea"))
expect { exception.raise() }.to(raiseException(
named: NSInternalInconsistencyException,
reason: "Not enough fish in the sea",
userInfo: ["something": "is fishy"]))
Objective-C的工作方式相同,但必须在没有返回值的表达式上进行期待时使用expectAction
宏。
// Objective-C
NSException *exception = [NSException exceptionWithName:NSInternalInconsistencyException
reason:@"Not enough fish in the sea."
userInfo:nil];
expectAction(^{ [exception raise]; }).to(raiseException());
// Use the property-block syntax to be more specific.
expectAction(^{ [exception raise]; }).to(raiseException().named(NSInternalInconsistencyException));
expectAction(^{ [exception raise]; }).to(raiseException().
named(NSInternalInconsistencyException).
reason("Not enough fish in the sea"));
expectAction(^{ [exception raise]; }).to(raiseException().
named(NSInternalInconsistencyException).
reason("Not enough fish in the sea").
userInfo(@{@"something": @"is fishy"}));
// You can also pass a block for custom matching of the raised exception
expectAction(exception.raise()).to(raiseException().satisfyingBlock(^(NSException *exception) {
expect(exception.name).to(beginWith(NSInternalInconsistencyException));
}));
某些测试框架使测试原始的C语言值变得困难。在Nimble中,这很简单
// Swift
let actual: CInt = 1
let expectedValue: CInt = 1
expect(actual).to(equal(expectedValue))
实际上,Nimble使用类型推断,因此你可以写下上述内容而不需要显式指定两种类型
// Swift
expect(1 as CInt).to(equal(1))
在Objective-C中,Nimble只支持Objective-C对象。为了测试原始的C语言值,请将它们包装在对象字面量中。
expect(@(1 + 1)).to(equal(@2));
在Nimble中,轻松地对异步更新的值进行期待。只需使用toEventually
或toEventuallyNot
。
// Swift
dispatch_async(dispatch_get_main_queue()) {
ocean.add("dolphins")
ocean.add("whales")
}
expect(ocean).toEventually(contain("dolphins", "whales"))
// Objective-C
dispatch_async(dispatch_get_main_queue(), ^{
[ocean add:@"dolphins"];
[ocean add:@"whales"];
});
expect(ocean).toEventually(contain(@"dolphins", @"whales"));
注意:toEventually在其主线程上触发轮询。阻塞主线程会导致Nimble停止运行循环。这可能会污染主线程上运行的不完整代码。阻塞主线程可能是由于阻塞I/O、调用sleep()、死锁和同步IPC导致的。
在上面的示例中,ocean
被持续重新评估。如果它包含海豚和鲸鱼,期待通过。如果ocean
仍然不包含它们,即使经过连续一秒钟的重新评估,期待也会失败。
有时一个值的更新可能需要超过一秒钟。在这种情况下,请使用timeout
参数。
// Swift
// Waits three seconds for ocean to contain "starfish":
expect(ocean).toEventually(contain("starfish"), timeout: 3)
// Objective-C
// Waits three seconds for ocean to contain "starfish":
expect(ocean).withTimeout(3).toEventually(contain(@"starfish"));
您还可以通过使用waitUntil
函数提供回调。
// Swift
waitUntil { done in
// do some stuff that takes a while...
NSThread.sleepForTimeInterval(0.5)
done()
}
// Objective-C
waitUntil(^(void (^done)(void)){
// do some stuff that takes a while...
[NSThread sleepForTimeInterval:0.5];
done();
});
waitUntil
也可选地接受一个超时参数
// Swift
waitUntil(timeout: 10) { done in
// do some stuff that takes a while...
NSThread.sleepForTimeInterval(1)
done()
}
// Objective-C
waitUntilTimeout(10, ^(void (^done)(void)){
// do some stuff that takes a while...
[NSThread sleepForTimeInterval:1];
done();
});
注意:waitUntil在主线程上触发其超时代码。阻塞主线程会导致Nimble停止运行循环以继续。这可能会污染主线程上运行的不完整代码。阻塞主线程可能是由于阻塞I/O、调用sleep()、死锁和同步IPC导致的。
在某些情况下(例如,在较慢的机器上运行时),修改默认的超时和轮询间隔值可能很有用。可以通过以下方式完成:
// Swift
// Increase the global timeout to 5 seconds:
Nimble.AsyncDefaults.Timeout = 5
// Slow the polling interval to 0.1 seconds:
Nimble.AsyncDefaults.PollInterval = 0.1
Nimble完全支持Objective-C。但在使用Nimble时,有几点需要注意:
传递给expect
函数的所有参数,以及如equal
之类的匹配器函数都必须是Objective-C对象。
// Objective-C
@import Nimble;
expect(@(1 + 1)).to(equal(@2));
expect(@"Hello world").to(contain(@"world"));
对于不返回值的表达式(例如-[NSException raise]
),请使用expectAction
而不是expect
来设置期望。
// Objective-C
expectAction(^{ [exception raise]; }).to(raiseException());
Nimble通过expect
函数提供了一种表达期望的简写。要在Objective-C中禁用此简写,请在导入Nimble之前,在代码中某处定义NIMBLE_DISABLE_SHORT_SYNTAX
宏。
#define NIMBLE_DISABLE_SHORT_SYNTAX 1
@import Nimble;
NMB_expect(^{ return seagull.squawk; }, __FILE__, __LINE__).to(NMB_equal(@"Squee!"));
如果您测试的函数名称与Nimble函数冲突,例如
expect
或equal
,则禁用简写非常有用。如果不是这种情况,没有理由禁用简写。
Nimble包含大量匹配器函数。
// Swift
// Passes if actual is equivalent to expected:
expect(actual).to(equal(expected))
expect(actual) == expected
// Passes if actual is not equivalent to expected:
expect(actual).toNot(equal(expected))
expect(actual) != expected
// Objective-C
// Passes if actual is equivalent to expected:
expect(actual).to(equal(expected))
// Passes if actual is not equivalent to expected:
expect(actual).toNot(equal(expected))
值必须是Equatable
、Comparable
或NSObject
的子类。equal
在比较一个或多个nil
值时总会失败。
// Swift
// Passes if actual has the same pointer address as expected:
expect(actual).to(beIdenticalTo(expected))
expect(actual) === expected
// Passes if actual does not have the same pointer address as expected:
expect(actual).toNot(beIdenticalTo(expected))
expect(actual) !== expected
要记住,只有当比较具有引用语义的类型时,beIdenticalTo
才有意义,这些类型具有身份概念。在Swift中,这意味着一个class
。此匹配器不适用于如struct
或enum
之类的具有值语义的类型。如果需要比较两个值类型,可以比较单独的属性,或者如果这样做有意义,则可以使其类型实现Equatable
并使用Nimble的等价匹配器。
// Objective-C
// Passes if actual has the same pointer address as expected:
expect(actual).to(beIdenticalTo(expected));
// Passes if actual does not have the same pointer address as expected:
expect(actual).toNot(beIdenticalTo(expected));
// Swift
expect(actual).to(beLessThan(expected))
expect(actual) < expected
expect(actual).to(beLessThanOrEqualTo(expected))
expect(actual) <= expected
expect(actual).to(beGreaterThan(expected))
expect(actual) > expected
expect(actual).to(beGreaterThanOrEqualTo(expected))
expect(actual) >= expected
// Objective-C
expect(actual).to(beLessThan(expected));
expect(actual).to(beLessThanOrEqualTo(expected));
expect(actual).to(beGreaterThan(expected));
expect(actual).to(beGreaterThanOrEqualTo(expected));
上面的比较匹配器必须实现
Comparable
。
由于计算机如何表示浮点数,有时两个浮点数相等的主张可能会失败。要表达两个数字在一定的误差范围内很接近,请使用beCloseTo
。
// Swift
expect(actual).to(beCloseTo(expected, within: delta))
// Objective-C
expect(actual).to(beCloseTo(expected).within(delta));
例如,为了断言10.01
接近于10
,可以写:
// Swift
expect(10.01).to(beCloseTo(10, within: 0.1))
// Objective-C
expect(@(10.01)).to(beCloseTo(@10).within(0.1));
Swift还提供了一种运算符快捷方式:
// Swift
expect(actual) ≈ expected
expect(actual) ≈ (expected, delta)
(在US键盘上通过按Option-x获得≈)
此处使用默认delta 0.0001。还有另一种方式来完成此操作
// Swift
expect(actual) ≈ expected ± delta
expect(actual) == expected ± delta
(在US键盘上按Option-Shift-=获得±)
如果您正在比较浮点数字组,以下可能会有所帮助:
// Swift
expect([0.0, 2.0]) ≈ [0.0001, 2.0001]
expect([0.0, 2.0]).to(beCloseTo([0.1, 2.1], within: 0.1))
传递给
beCloseTo
匹配器的值必须可以转换为Double
。
// Swift
// Passes if instance is an instance of aClass:
expect(instance).to(beAnInstanceOf(aClass))
// Passes if instance is an instance of aClass or any of its subclasses:
expect(instance).to(beAKindOf(aClass))
// Objective-C
// Passes if instance is an instance of aClass:
expect(instance).to(beAnInstanceOf(aClass));
// Passes if instance is an instance of aClass or any of its subclasses:
expect(instance).to(beAKindOf(aClass));
实例必须是Objective-C对象:是
NSObject
的子类,或用@objc
前缀桥接到Objective-C的Swift对象。
例如,为了断言dolphin
是Mammal
的一种
// Swift
expect(dolphin).to(beAKindOf(Mammal))
// Objective-C
expect(dolphin).to(beAKindOf([Mammal class]));
beAnInstanceOf
使用-[NSObject isMemberOfClass:]
方法来测试成员资格。beAKindOf
使用-[NSObject isKindOfClass:]
。
// Passes if actual is not nil, true, or an object with a boolean value of true:
expect(actual).to(beTruthy())
// Passes if actual is only true (not nil or an object conforming to BooleanType true):
expect(actual).to(beTrue())
// Passes if actual is nil, false, or an object with a boolean value of false:
expect(actual).to(beFalsy())
// Passes if actual is only false (not nil or an object conforming to BooleanType false):
expect(actual).to(beFalse())
// Passes if actual is nil:
expect(actual).to(beNil())
// Objective-C
// Passes if actual is not nil, true, or an object with a boolean value of true:
expect(actual).to(beTruthy());
// Passes if actual is only true (not nil or an object conforming to BooleanType true):
expect(actual).to(beTrue());
// Passes if actual is nil, false, or an object with a boolean value of false:
expect(actual).to(beFalsy());
// Passes if actual is only false (not nil or an object conforming to BooleanType false):
expect(actual).to(beFalse());
// Passes if actual is nil:
expect(actual).to(beNil());
如果你正在使用 Swift 2.0 及以上版本,你可以使用 throwError
断言来检查是否抛出了错误。
// Swift
// Passes if somethingThatThrows() throws an ErrorType:
expect{ try somethingThatThrows() }.to(throwError())
// Passes if somethingThatThrows() throws an error with a given domain:
expect{ try somethingThatThrows() }.to(throwError { (error: ErrorType) in
expect(error._domain).to(equal(NSCocoaErrorDomain))
})
// Passes if somethingThatThrows() throws an error with a given case:
expect{ try somethingThatThrows() }.to(throwError(NSCocoaError.PropertyListReadCorruptError))
// Passes if somethingThatThrows() throws an error with a given type:
expect{ try somethingThatThrows() }.to(throwError(errorType: MyError.self))
如果你直接使用 ErrorType
值,就像使用 Result
或 Promise
类型时那样,你可以使用 matchError
断言来检查错误是否为预期的错误,无需进行显式转换。
// Swift
let actual: ErrorType = …
// Passes if actual contains any error value from the MyErrorEnum type:
expect(actual).to(matchError(MyErrorEnum))
// Passes if actual contains the Timeout value from the MyErrorEnum type:
expect(actual).to(matchError(MyErrorEnum.Timeout))
// Passes if actual contains an NSError equal to the given one:
expect(actual).to(matchError(NSError(domain: "err", code: 123, userInfo: nil)))
注意:此功能仅限于 Swift 使用。
// Swift
// Passes if actual, when evaluated, raises an exception:
expect(actual).to(raiseException())
// Passes if actual raises an exception with the given name:
expect(actual).to(raiseException(named: name))
// Passes if actual raises an exception with the given name and reason:
expect(actual).to(raiseException(named: name, reason: reason))
// Passes if actual raises an exception and it passes expectations in the block
// (in this case, if name begins with 'a r')
expect { exception.raise() }.to(raiseException { (exception: NSException) in
expect(exception.name).to(beginWith("a r"))
})
// Objective-C
// Passes if actual, when evaluated, raises an exception:
expect(actual).to(raiseException())
// Passes if actual raises an exception with the given name
expect(actual).to(raiseException().named(name))
// Passes if actual raises an exception with the given name and reason:
expect(actual).to(raiseException().named(name).reason(reason))
// Passes if actual raises an exception and it passes expectations in the block
// (in this case, if name begins with 'a r')
expect(actual).to(raiseException().satisfyingBlock(^(NSException *exception) {
expect(exception.name).to(beginWith(@"a r"));
}));
注意:目前 Swift 没有异常(见 #220)。只有 Objective-C 代码可以抛出 Nimble 能够捕获的异常。
// Swift
// Passes if all of the expected values are members of actual:
expect(actual).to(contain(expected...))
// Passes if actual is an empty collection (it contains no elements):
expect(actual).to(beEmpty())
// Objective-C
// Passes if expected is a member of actual:
expect(actual).to(contain(expected));
// Passes if actual is an empty collection (it contains no elements):
expect(actual).to(beEmpty());
在 Swift 中,
contain
可以接受任意数量的参数。如果所有这些参数都是集合的成员,则期望通过。就现在而言,在 Objective-C 中,contain
只能接受一个参数(目前)。
例如,为了断言某个海洋生物名称的列表包含“海豚”和“海星”
// Swift
expect(["whale", "dolphin", "starfish"]).to(contain("dolphin", "starfish"))
// Objective-C
expect(@[@"whale", @"dolphin", @"starfish"]).to(contain(@"dolphin"));
expect(@[@"whale", @"dolphin", @"starfish"]).to(contain(@"starfish"));
contain
和beEmpty
期望集合为NSArray
、NSSet
或由Equatable
元素组成的 Swift 集合的实例。
若要测试一组元素是否位于有序集合的起始或结束位置,请使用 beginWith
和 endWith
// Swift
// Passes if the elements in expected appear at the beginning of actual:
expect(actual).to(beginWith(expected...))
// Passes if the the elements in expected come at the end of actual:
expect(actual).to(endWith(expected...))
// Objective-C
// Passes if the elements in expected appear at the beginning of actual:
expect(actual).to(beginWith(expected));
// Passes if the the elements in expected come at the end of actual:
expect(actual).to(endWith(expected));
beginWith
和endWith
期望集合是NSArray
或由Equatable
元素组成的有序 Swift 集合的实例。
与 contain
类似,在 Objective-C 中,beginWith
和 endWith
只支持一个参数 (目前)。
// Swift
// Passes if actual contains substring expected:
expect(actual).to(contain(expected))
// Passes if actual begins with substring:
expect(actual).to(beginWith(expected))
// Passes if actual ends with substring:
expect(actual).to(endWith(expected))
// Passes if actual is an empty string, "":
expect(actual).to(beEmpty())
// Passes if actual matches the regular expression defined in expected:
expect(actual).to(match(expected))
// Objective-C
// Passes if actual contains substring expected:
expect(actual).to(contain(expected));
// Passes if actual begins with substring:
expect(actual).to(beginWith(expected));
// Passes if actual ends with substring:
expect(actual).to(endWith(expected));
// Passes if actual is an empty string, "":
expect(actual).to(beEmpty());
// Passes if actual matches the regular expression defined in expected:
expect(actual).to(match(expected))
// Swift
// with a custom function:
expect([1,2,3,4]).to(allPass({$0 < 5}))
// with another matcher:
expect([1,2,3,4]).to(allPass(beLessThan(5)))
// Objective-C
expect(@[@1, @2, @3,@4]).to(allPass(beLessThan(@5)));
对于 Swift,实际值必须是 SequenceType
,例如数组、集合或自定义序列类型。
对于 Objective-C,实际值必须是 NSFastEnumeration
,例如 NSArray
和 NSSet
,或者 NSObjects 的其中一种变体,并且此处只可用另一种断言器。
// passes if actual collection's count is equal to expected
expect(actual).to(haveCount(expected))
// passes if actual collection's count is not equal to expected
expect(actual).notTo(haveCount(expected))
// passes if actual collection's count is equal to expected
expect(actual).to(haveCount(expected))
// passes if actual collection's count is not equal to expected
expect(actual).notTo(haveCount(expected))
对于 Swift,实际值必须是诸如数组、字典或集合之类的 CollectionType
。
对于 Objective-C,实际值必须是以下类之一:NSArray
、NSDictionary
、NSSet
、NSHashTable
或它们的子类之一。
// passes if actual is either less than 10 or greater than 20
expect(actual).to(satisfyAnyOf(beLessThan(10), beGreaterThan(20)))
// can include any number of matchers -- the following will pass
// **be careful** -- too many matchers can be the sign of an unfocused test
expect(6).to(satisfyAnyOf(equal(2), equal(3), equal(4), equal(5), equal(6), equal(7)))
// in Swift you also have the option to use the || operator to achieve a similar function
expect(82).to(beLessThan(50) || beGreaterThan(80))
// passes if actual is either less than 10 or greater than 20
expect(actual).to(satisfyAnyOf(beLessThan(@10), beGreaterThan(@20)))
// can include any number of matchers -- the following will pass
// **be careful** -- too many matchers can be the sign of an unfocused test
expect(@6).to(satisfyAnyOf(equal(@2), equal(@3), equal(@4), equal(@5), equal(@6), equal(@7)))
注意:此断言器允许你将任意数量的断言器链起来。这提供了灵活性,但如果你发现自己在单个测试中多次链多个断言器,考虑将单一测试重构为多个更专注的测试,以实现更好的覆盖。
你可以通过重构将单一测试分解为多个,以便更好地进行测试覆盖。
在 Nimble 中,断言器是接受预期值并返回一个 MatcherFunc
闭包的 Swift 函数。以 equal
为例:
// Swift
public func equal<T: Equatable>(expectedValue: T?) -> MatcherFunc<T?> {
return MatcherFunc { actualExpression, failureMessage in
failureMessage.postfixMessage = "equal <\(expectedValue)>"
return actualExpression.evaluate() == expectedValue
}
}
MatcherFunc
闭包的返回值是一个 Bool
类型,表示实际值是否与期望匹配:如果匹配则为 true
,如果不匹配则为 false
。
实际的
equal
匹配函数在actual
或expected
为nil时不会匹配;上面示例已编辑以节省空间。
因为匹配器只是Swift函数,所以您可以在任何地方定义它们:在测试文件开头、所有测试共享的文件中,或者您分发给他人的Xcode项目中。
如果您编写了一个认为每个人都可以使用的匹配器,请考虑通过发送pull request将其添加到Nimble的内置匹配器集中!或者通过GitHub自行分发。
要了解如何编写自己的匹配器,请查看Matchers
目录以了解Nimble内置匹配器集的实现。您还可以查看下面的提示。
actualExpression
是围绕提供给expect
函数的值的一个懒加载、记忆化闭包。该表达式可以是闭包或直接传递给expect(...)
的值。为了确定该值是否匹配,自定义匹配器应该调用actualExpression.evaluate()
// Swift
public func beNil<T>() -> MatcherFunc<T?> {
return MatcherFunc { actualExpression, failureMessage in
failureMessage.postfixMessage = "be nil"
return actualExpression.evaluate() == nil
}
}
在上面的例子中,actualExpression
不是nil
- 它是一个返回值的闭包。它通过访问evaluate()
方法返回的值,这个值可能为nil
。如果该值为nil
,则beNil
匹配器函数返回true
,表明期望通过。
使用expression.isClosure
来确定表达式是否将调用闭包以产生其值。
使用Swift的泛型,匹配器可以通过修改返回类型来约束传递给expect
函数的实际值的类型。
例如,以下匹配器haveDescription
仅接受实现Printable
协议的实际值。它检查它们的description
与提供给匹配器函数的值,如果它们相同则通过。
// Swift
public func haveDescription(description: String) -> MatcherFunc<Printable?> {
return MatcherFunc { actual, failureMessage in
return actual.evaluate().description == description
}
}
默认情况下,当期望失败时,Nimble输出以下失败消息
expected to match, got <\(actual)>
您可以通过从您的MatcherFunc
闭包中修改failureMessage
结构来自定义此消息。要改变动词“match”,请更新postfixMessage
属性
// Swift
// Outputs: expected to be under the sea, got <\(actual)>
failureMessage.postfixMessage = "be under the sea"
您可以通过更新failureMessage.actualValue
来更改actual
值的显示方式。或者,如果要完全删除它,将其设置为nil
// Swift
// Outputs: expected to be under the sea
failureMessage.actualValue = nil
failureMessage.postfixMessage = "be under the sea"
要从Objective-C使用用Swift编写的自定义匹配器,您将不得不扩展NMBObjCMatcher
类,为您的自定义匹配器添加一个新的类方法。以下示例定义了类方法+[NMBObjCMatcher beNilMatcher]
// Swift
extension NMBObjCMatcher {
public class func beNilMatcher() -> NMBObjCMatcher {
return NMBObjCMatcher { actualBlock, failureMessage, location in
let block = ({ actualBlock() as NSObject? })
let expr = Expression(expression: block, location: location)
return beNil().matches(expr, failureMessage: failureMessage)
}
}
}
这允许您从Objective-C使用匹配器
// Objective-C
expect(actual).to([NMBObjCMatcher beNilMatcher]());
为了使语法更容易使用,请定义一个调用类方法的C函数
// Objective-C
FOUNDATION_EXPORT id<NMBMatcher> beNil() {
return [NMBObjCMatcher beNilMatcher];
}
nil
当支持Objective-C时,请确保您适当地处理nil
。像Cedar一样,**大多数匹配器在nil上不匹配**。这是为了防止测试编写者在没有预期到nil
值时感到惊讶。
Nimble为想要对nil
对象进行期望的测试编写者提供了beNil
匹配器函数。
// Objective-C
expect(nil).to(equal(nil)); // fails
expect(nil).to(beNil()); // passes
如果您的匹配器不想与nil匹配,您将使用NonNilMatcherFunc
以及使用在NMBObjCMatcher
上的canMatchNil
构造函数。当它们为nil时,使用这两种类型将会自动生成预期的值失败信息。
public func beginWith<S: SequenceType, T: Equatable where S.Generator.Element == T>(startingElement: T) -> NonNilMatcherFunc<S> {
return NonNilMatcherFunc { actualExpression, failureMessage in
failureMessage.postfixMessage = "begin with <\(startingElement)>"
if let actualValue = actualExpression.evaluate() {
var actualGenerator = actualValue.generate()
return actualGenerator.next() == startingElement
}
return false
}
}
extension NMBObjCMatcher {
public class func beginWithMatcher(expected: AnyObject) -> NMBObjCMatcher {
return NMBObjCMatcher(canMatchNil: false) { actualExpression, failureMessage in
let actual = actualExpression.evaluate()
let expr = actualExpression.cast { $0 as? NMBOrderedCollection }
return beginWith(expected).matches(expr, failureMessage: failureMessage)
}
}
}
Nimble可以单独使用,也可以与它的姊妹项目Quick一起使用。要安装Quick和Nimble,请遵循Quick README中的安装说明。
Nimble目前可以通过两种方式安装:使用CocoaPods或者通过git子模块。
要将Nimble作为子模块使用来测试您的iOS或OS X应用程序,请遵循以下4个简单步骤:
有关这些步骤的更详细说明,请阅读如何安装Quick。为了仅安装Nimble,请忽略涉及将Quick添加到您项目的步骤。
Nimble与XCTest集成,以便在Xcode测试包中使用时表现良好,但它也可以在独立应用程序中使用。使用上述任何一种方法安装Nimble后,还需要执行两个额外的步骤才能使其工作。
NimbleAssertionHandler
。例如class MyAssertionHandler : AssertionHandler {
func assert(assertion: Bool, message: FailureMessage, location: SourceLocation) {
if (!assertion) {
print("Expectation failed: \(message.stringValue)")
}
}
}
// Somewhere before you use any assertions
NimbleAssertionHandler = MyAssertionHandler()
rm "${SWIFT_STDLIB_TOOL_DESTINATION_DIR}/libswiftXCTest.dylib"
现在,您可以在代码中使用Nimble断言,并按照您的想法处理失败。