Nimble
使用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())
如何使用Nimble
目录 由DocToc生成
一些背景:在使用XCTest中使用断言表达结果
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断言有几个缺点
- 宏不够。没有简单的方法来断言一个字符串是否包含特定的子字符串,或者一个数字是否小于或等于另一个数字。
- 难以编写异步测试。XCTest强制您编写大量的模板代码。
Nimble解决了这些问题。
expect(...).to
进行期望
Nimble:使用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中使用此语法。
💔
延迟计算值
当期匹配时,expect
函数不会评估其给出的值。所以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原语
一些测试框架使测试原生的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));
Async/Await 支持
Nimble 使得等待异步函数完成变得非常简单。只需将异步函数传入 expect
即可。
// Swift
await expect { await aFunctionReturning1() }.to(equal(1))
首先等待异步函数执行,然后再将其传递给匹配器。这使得匹配器可以像以前一样运行同步代码,而不关心它处理的值是异步获取的还是同步获取的。
异步支持仅限 Swift,并且需要在异步上下文中执行测试。对于 XCTest,这就像为测试函数标记上 async
一样简单。如果您使用 Quick,Quick 6 中所有测试都在异步上下文中执行。在 Quick 7 及以后的版本中,只有位于 AsyncSpec
子类中的测试才会以异步上下文执行。
为了避免在异步上下文中使用同步 expect
时出现编译器错误,带有异步表达式的 expect
不支持 autoclosures。然而,提供了 expecta
(expect async)函数作为替代方案,它支持 autoclosures。
// Swift
await expecta(await aFunctionReturning1()).to(equal(1)))
类似地,如果您需要在某种情况下强制编译器生成 SyncExpectation
,可以使用 expects
(expect sync)函数来生成 SyncExpectation
。例如:
// Swift
expects(someNonAsyncFunction()).to(equal(1)))
expects(await someAsyncFunction()).to(equal(1)) // Compiler error: 'async' call in an autoclosure that does not support concurrency
异步匹配器
除了在将异步函数传递给同步谓词之前对其断言之外,您还可以编写直接接受异步值的匹配器。这些被称为 AsyncPredicate
。这在直接对演员进行断言时最为明显。除了编写自己的异步匹配器外,Nimble 目前还提供以下谓词的异步版本:
allPass
containElementSatisfying
satisfyAllOf
和&&
操作符重载同时接受AsyncPredicate
和同步Predicate
。satisfyAnyOf
和||
操作符重载同时接受AsyncPredicate
和同步Predicate
。
注意:Async/Await 支持与下面描述的 toEventually
/toEventuallyNot
功能不同。
轮询期望
在 Nimble 中,对异步更新的值设置期望非常简单。只需使用 toEventually
或 toEventuallyNot
// Swift
DispatchQueue.main.async {
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
仍然不包含它们,则期望失败。
在异步测试中使用轮询期望
您还可以在异步上下文中轻松使用 toEventually
或 toEventuallyNot
。您只需要在行的开头添加一个 await
语句
// Swift
DispatchQueue.main.async {
ocean.add("dolphins")
ocean.add("whales")
}
await expect(ocean).toEventually(contain("dolphens", "whiles"))
从 Nimble 12 版本开始,toEventually
等。现在也支持异步期望。以下示例现在受到支持
actor MyActor {
private var counter = 0
func access() -> Int {
counter += 1
return counter
}
}
let subject = MyActor()
await expect { await subject.access() }.toEventually(equal(2))
验证谓词是否永远不会或始终匹配
您还可以测试值在超时期间始终或永远不会匹配。使用 toNever
和 toAlways
进行此测试
// Swift
ocean.add("dolphins")
expect(ocean).toAlways(contain("dolphins"))
expect(ocean).toNever(contain("hares"))
// Objective-C
[ocean add:@"dolphins"]
expect(ocean).toAlways(contain(@"dolphins"))
expect(ocean).toNever(contain(@"hares"))
等待回调被调用
您还可以使用 waitUntil
函数提供回调
// Swift
waitUntil { done in
ocean.goFish { success in
expect(success).to(beTrue())
done()
}
}
// Objective-C
waitUntil(^(void (^done)(void)){
[ocean goFishWithHandler:^(BOOL success){
expect(success).to(beTrue());
done();
}];
});
waitUntil
还可以可选地接受一个超时参数
// Swift
waitUntil(timeout: .seconds(10)) { done in
ocean.goFish { success in
expect(success).to(beTrue())
done()
}
}
// Objective-C
waitUntilTimeout(10, ^(void (^done)(void)){
[ocean goFishWithHandler:^(BOOL success){
expect(success).to(beTrue());
done();
}];
});
注意:waitUntil
在主线程上触发其超时代码。阻塞主线程会导致 Nimble 继续停止运行循环。这可能会导致正在主线程上运行的任何未完成代码的测试污染。阻塞主线程可能是由于阻塞 I/O、sleep() 调用、死锁和同步 IPC 导致的。
修改超时和轮询间隔
有时候,一个值更新可能需要超过一秒钟。在这些情况下,可以使用timeout
参数。
// Swift
// Waits three seconds for ocean to contain "starfish":
expect(ocean).toEventually(contain("starfish"), timeout: .seconds(3))
// Evaluate someValue every 0.2 seconds repeatedly until it equals 100, or fails if it timeouts after 5.5 seconds.
expect(someValue).toEventually(equal(100), timeout: .milliseconds(5500), pollInterval: .milliseconds(200))
// Objective-C
// Waits three seconds for ocean to contain "starfish":
expect(ocean).withTimeout(3).toEventually(contain(@"starfish"));
修改默认超时和轮询间隔
在某些情况下(例如在较慢的机器上运行时),修改默认超时和轮询间隔值可能很有用。可以通过以下方式完成。
// Swift
// Increase the global timeout to 5 seconds:
Nimble.PollingDefaults.timeout = .seconds(5)
// Slow the polling interval to 0.1 seconds:
Nimble.PollingDefaults.pollInterval = .milliseconds(100)
您可以在启动测试时全局设置这些参数有两种方法。
快速
如果您使用Quick,请添加一个设置您期望的PollingDefaults
的QuickConfiguration
子类。
import Quick
import Nimble
class PollingConfiguration: QuickConfiguration {
override class func configure(_ configuration: QCKConfiguration) {
Nimble.PollingDefaults.timeout = .seconds(5)
Nimble.PollingDefaults.pollInterval = .milliseconds(100)
}
}
XCTest
如果您使用XCTest,请添加一个符合XCTestObservation
的对象并实现testBundleWillStart(_:)
。
另外,您需要在测试启动时使用XCTestObservationCenter
注册此观察者。为此,在您的测试包的Info.plist中设置NSPrincipalClass
键,并实现同名类。
例如
<!-- Info.plist -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- ... -->
<key>NSPrincipalClass</key>
<string>MyTests.TestSetup</string>
</dict>
</plist>
// TestSetup.swift
import XCTest
import Nimble
@objc
class TestSetup: NSObject {
override init() {
XCTestObservationCenter.shared.register(PollingConfigurationTestObserver())
}
}
class PollingConfigurationTestObserver: NSObject, XCTestObserver {
func testBundleWillStart(_ testBundle: Bundle) {
Nimble.PollingDefaults.timeout = .seconds(5)
Nimble.PollingDefaults.pollInterval = .milliseconds(100)
}
}
在Linux中,您可以在调用LinuxMain
来设置轮询默认值。
Objective-C 支持
Nimble 对 Objective-C 完全支持。然而,在使用 Nimble 进行 Objective-C 开发时,需要注意以下两点:
-
传递给
expect
函数的所有参数,以及像equal
这样的匹配函数,必须是 Objective-C 对象,或者可以被转换为NSObject
等效类型。// Objective-C @import Nimble; expect(@(1 + 1)).to(equal(@2)); expect(@"Hello world").to(contain(@"world")); // Boxed as NSNumber * expect(2).to(equal(2)); expect(1.2).to(beLessThan(2.0)); expect(true).to(beTruthy()); // Boxed as NSString * expect("Hello world").to(equal("Hello world")); // Boxed as NSRange expect(NSMakeRange(1, 10)).to(equal(NSMakeRange(1, 10)));
-
对于不返回值的表达式,如
-[NSException raise]
,请使用expectAction
而不是expect
来设置期望。// Objective-C expectAction(^{ [exception raise]; }).to(raiseException());
以下类型的值将转换为 NSObject
类型:
- 数值类型 转换为
NSNumber *
。 NSRange
转换为NSValue *
。char *
转换为NSString *
。
以下匹配器:
equal
beGreaterThan
beGreaterThanOrEqual
beLessThan
beLessThanOrEqual
beCloseTo
beTrue
beFalse
beTruthy
beFalsy
haveCount
如需更多信息,请 提交问题。
禁用 Objective-C 简写符号
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 包含了丰富的匹配函数。
类型检查
Nimble 支持检查任何类型的对象的类型成员,无论是否是 Objective-C 兼容的。
// Swift
protocol SomeProtocol{}
class SomeClassConformingToProtocol: SomeProtocol{}
struct SomeStructConformingToProtocol: SomeProtocol{}
// The following tests pass
expect(1).to(beAKindOf(Int.self))
expect("turtle").to(beAKindOf(String.self))
let classObject = SomeClassConformingToProtocol()
expect(classObject).to(beAKindOf(SomeProtocol.self))
expect(classObject).to(beAKindOf(SomeClassConformingToProtocol.self))
expect(classObject).toNot(beAKindOf(SomeStructConformingToProtocol.self))
let structObject = SomeStructConformingToProtocol()
expect(structObject).to(beAKindOf(SomeProtocol.self))
expect(structObject).to(beAKindOf(SomeStructConformingToProtocol.self))
expect(structObject).toNot(beAKindOf(SomeClassConformingToProtocol.self))
// Objective-C
// The following tests pass
NSMutableArray *array = [NSMutableArray array];
expect(array).to(beAKindOf([NSArray class]));
expect(@1).toNot(beAKindOf([NSNull class]));
可以使用 beAnInstanceOf
匹配器来测试对象的确切类型。
// Swift
protocol SomeProtocol{}
class SomeClassConformingToProtocol: SomeProtocol{}
struct SomeStructConformingToProtocol: SomeProtocol{}
// Unlike the 'beKindOf' matcher, the 'beAnInstanceOf' matcher only
// passes if the object is the EXACT type requested. The following
// tests pass -- note its behavior when working in an inheritance hierarchy.
expect(1).to(beAnInstanceOf(Int.self))
expect("turtle").to(beAnInstanceOf(String.self))
let classObject = SomeClassConformingToProtocol()
expect(classObject).toNot(beAnInstanceOf(SomeProtocol.self))
expect(classObject).to(beAnInstanceOf(SomeClassConformingToProtocol.self))
expect(classObject).toNot(beAnInstanceOf(SomeStructConformingToProtocol.self))
let structObject = SomeStructConformingToProtocol()
expect(structObject).toNot(beAnInstanceOf(SomeProtocol.self))
expect(structObject).to(beAnInstanceOf(SomeStructConformingToProtocol.self))
expect(structObject).toNot(beAnInstanceOf(SomeClassConformingToProtocol.self))
等价性
// 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
来比较一个或多个 null
值时,总是会失败。
身份
// 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
的类型)时,此匹配器将不会生效。如果您需要比较两个值类型,请考虑您的类型实例 Identical 的含义。这可能意味着比较单个属性,或者如果这样做有意义,使类型符合 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)
(在美式键盘上,通过按下 option+x 来获取 ≈
)
前一个版本使用默认的 delta 值 0.0001。这里还有一种方法来做这个操作
// Swift
expect(actual) ≈ expected ± delta
expect(actual) == expected ± delta
(在美式键盘上,通过按下 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
匹配器的值必须符合FloatingPoint
。
类型/类
// 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 对象。
例如,要断言 海豚
是 哺乳动物
的一种
// 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 Boolean 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 Boolean 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 Boolean 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 Boolean false):
expect(actual).to(beFalse());
// Passes if 'actual' is nil:
expect(actual).to(beNil());
Swift 断言
如果您使用 Swift,可以使用 throwAssertion
匹配器来检查是否引发了断言(例如 fatalError()
)。这得益于 @mattgallagher 的 CwlPreconditionTesting 库。
// Swift
// Passes if 'somethingThatThrows()' throws an assertion,
// such as by calling 'fatalError()' or if a precondition fails:
expect { try somethingThatThrows() }.to(throwAssertion())
expect { () -> Void in fatalError() }.to(throwAssertion())
expect { precondition(false) }.to(throwAssertion())
// Passes if throwing an NSError is not equal to throwing an assertion:
expect { throw NSError(domain: "test", code: 0, userInfo: nil) }.toNot(throwAssertion())
// Passes if the code after the precondition check is not run:
var reachedPoint1 = false
var reachedPoint2 = false
expect {
reachedPoint1 = true
precondition(false, "condition message")
reachedPoint2 = true
}.to(throwAssertion())
expect(reachedPoint1) == true
expect(reachedPoint2) == false
注意
- 该功能仅在 Swift 中可用。
- tvOS 模拟器支持,但使用不同的机制,需要您关闭 tvOS 方案测试配置中的
Debug executable
方案设置。
Swift错误处理
您可以使用throwError
匹配器来检查是否抛出了错误。
// Swift
// Passes if 'somethingThatThrows()' throws an 'Error':
expect { try somethingThatThrows() }.to(throwError())
// Passes if 'somethingThatThrows()' throws an error within a particular domain:
expect { try somethingThatThrows() }.to(throwError { (error: Error) in
expect(error._domain).to(equal(NSCocoaErrorDomain))
})
// Passes if 'somethingThatThrows()' throws a particular error enum case:
expect { try somethingThatThrows() }.to(throwError(NSCocoaError.PropertyListReadCorruptError))
// Passes if 'somethingThatThrows()' throws an error of a particular type:
expect { try somethingThatThrows() }.to(throwError(errorType: NimbleError.self))
当直接使用Error
值时,使用matchError
匹配器允许您对错误本身执行某些检查,而无需显式地将错误转型。
matchError
匹配器允许您检查错误
- 是否与您期望的错误类型相同。
- 表示您期望的特定错误值。
这在使用Result
或Promise
类型时很有用,例如。
// Swift
let actual: Error = ...
// Passes if 'actual' represents any error value from the NimbleErrorEnum type:
expect(actual).to(matchError(NimbleErrorEnum.self))
// Passes if 'actual' represents the case 'timeout' from the NimbleErrorEnum type:
expect(actual).to(matchError(NimbleErrorEnum.timeout))
// Passes if 'actual' contains an NSError equal to the one provided:
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 which passes expectations defined in the given closure:
// (in this case, if the exception's 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 defined in the given 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 empty (i.e. 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 empty (i.e. it contains no elements):
expect(actual).to(beEmpty());
在Swift中,
contain
可以接受任意数量的参数。如果所有这些参数都是集合的成员,则期望通过。在Objective-C中,目前只接受一个参数暂时如此。
例如,要断言海洋生物名称列表包含“dolphin”和“starfish”
// 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
目前只支持单个参数。(更多信息请见此链接)。
对于返回无严格排序的复杂数组对象的代码,可以使用containElementSatisfying
匹配器。
// Swift
struct Turtle {
let color: String
}
let turtles: [Turtle] = functionThatReturnsSomeTurtlesInAnyOrder()
// This set of matchers passes regardless of whether the array is
// [{color: "blue"}, {color: "green"}] or [{color: "green"}, {color: "blue"}]:
expect(turtles).to(containElementSatisfying({ turtle in
return turtle.color == "green"
}))
expect(turtles).to(containElementSatisfying({ turtle in
return turtle.color == "blue"
}, "that is a turtle with color 'blue'"))
// The second matcher will incorporate the provided string in the error message
// should it fail
注意:在Swift中,containElementSatisfying
也有一个变体,它接受异步函数。
// Objective-C
@interface Turtle : NSObject
@property (nonatomic, readonly, nonnull) NSString *color;
@end
@implementation Turtle
@end
NSArray<Turtle *> * __nonnull turtles = functionThatReturnsSomeTurtlesInAnyOrder();
// This set of matchers passes regardless of whether the array is
// [{color: "blue"}, {color: "green"}] or [{color: "green"}, {color: "blue"}]:
expect(turtles).to(containElementSatisfying(^BOOL(id __nonnull object) {
return [[turtle color] isEqualToString:@"green"];
}));
expect(turtles).to(containElementSatisfying(^BOOL(id __nonnull object) {
return [[turtle color] isEqualToString:@"blue"];
}));
要断言给定的Comparable
值是否在Range
中,请使用beWithin
匹配器。
// Swift
// Passes if 5 is within the range 1 through 10, inclusive
expect(5).to(beWithin(1...10))
// Passes if 5 is not within the range 2 through 4.
expect(5).toNot(beWithin(2..<5))
字符串
// Swift
// Passes if 'actual' contains 'substring':
expect(actual).to(contain(substring))
// Passes if 'actual' begins with 'prefix':
expect(actual).to(beginWith(prefix))
// Passes if 'actual' ends with 'suffix':
expect(actual).to(endWith(suffix))
// Passes if 'actual' represents the 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':
expect(actual).to(contain(expected));
// Passes if 'actual' begins with 'prefix':
expect(actual).to(beginWith(prefix));
// Passes if 'actual' ends with 'suffix':
expect(actual).to(endWith(suffix));
// Passes if 'actual' represents the empty string, "":
expect(actual).to(beEmpty());
// Passes if 'actual' matches the regular expression defined in 'expected':
expect(actual).to(match(expected))
集合元素
Nimble提供了方法来检查集合中的所有元素都满足给定的预期。
Swift
在Swift中,集合必须是遵守Sequence
类型的一个实例。
// Swift
// Providing a custom function:
expect([1, 2, 3, 4]).to(allPass { $0 < 5 })
// Composing the expectation with another matcher:
expect([1, 2, 3, 4]).to(allPass(beLessThan(5)))
还有与异步匹配器相关的allPass
变体,并接受异步函数。
// Swift
// Providing a custom function:
expect([1, 2, 3, 4]).to(allPass { await asyncFunctionReturningBool($0) })
// Composing the expectation with another matcher:
expect([1, 2, 3, 4]).to(allPass(someAsyncMatcher()))
Objective-C
在Objective-C中,集合必须是实现NSFastEnumeration
协议的类型的一个实例,其元素必须是子类为NSObject
的类型的一个实例。
此外,与Swift不同,没有重写来指定自定义匹配器函数。
// Objective-C
expect(@[@1, @2, @3, @4]).to(allPass(beLessThan(@5)));
收藏数量
// Swift
// Passes if 'actual' contains the 'expected' number of elements:
expect(actual).to(haveCount(expected))
// Passes if 'actual' does _not_ contain the 'expected' number of elements:
expect(actual).notTo(haveCount(expected))
// Objective-C
// Passes if 'actual' contains the 'expected' number of elements:
expect(actual).to(haveCount(expected))
// Passes if 'actual' does _not_ contain the 'expected' number of elements:
expect(actual).notTo(haveCount(expected))
对于Swift,实际值必须是符合Collection
类型的实例。例如,Array
、Dictionary
或Set
的实例。
对于Objective-C,实际值必须是以下类之一,或它们的子类
,NSArray
,NSDictionary
NSSet
或
.NSHashTable
通知
// Swift
let testNotification = Notification(name: Notification.Name("Foo"), object: nil)
// Passes if the closure in expect { ... } posts a notification to the default
// notification center.
expect {
NotificationCenter.default.post(testNotification)
}.to(postNotifications(equal([testNotification])))
// Passes if the closure in expect { ... } posts a notification to a given
// notification center
let notificationCenter = NotificationCenter()
expect {
notificationCenter.post(testNotification)
}.to(postNotifications(equal([testNotification]), from: notificationCenter))
// Passes if the closure in expect { ... } posts a notification with the provided names to a given
// notification center. Make sure to use this when running tests on Catalina,
// using DistributedNotificationCenter as there is currently no way
// of observing notifications without providing specific names.
let distributedNotificationCenter = DistributedNotificationCenter()
expect {
distributedNotificationCenter.post(testNotification)
}.toEventually(postDistributedNotifications(equal([testNotification]),
from: distributedNotificationCenter,
names: [testNotification.name]))
此匹配器仅适用于Swift。
结果
// Swift
let aResult: Result<String, Error> = .success("Hooray")
// passes if result is .success
expect(aResult).to(beSuccess())
// passes if result value is .success and validates Success value
expect(aResult).to(beSuccess { value in
expect(value).to(equal("Hooray"))
})
enum AnError: Error {
case somethingHappened
}
let otherResult: Result<String, AnError> = .failure(.somethingHappened)
// passes if result is .failure
expect(otherResult).to(beFailure())
// passes if result value is .failure and validates error
expect(otherResult).to(beFailure { error in
expect(error).to(matchError(AnError.somethingHappened))
})
此匹配器仅适用于Swift。
将值匹配到匹配器组中的任意一个
// Swift
// 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))
注意:在Swift中,您可以通过使用satisfyAnyOf
/||
混合使用同步和异步谓词。
// Objective-C
// 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)))
注意:此匹配器允许您将任意数量的匹配器连接起来。这提供了灵活性,但如果您在一个测试中需要连接许多匹配器,请考虑是否可以将这个单一测试重构成多个更精确的测试,以获得更好的覆盖率。
自定义验证
// Swift
// passes if .succeeded is returned from the closure
expect {
guard case .enumCaseWithAssociatedValueThatIDontCareAbout = actual else {
return .failed(reason: "wrong enum case")
}
return .succeeded
}.to(succeed())
// passes if .failed is returned from the closure
expect {
guard case .enumCaseWithAssociatedValueThatIDontCareAbout = actual else {
return .failed(reason: "wrong enum case")
}
return .succeeded
}.notTo(succeed())
当测试失败时,使用.failed()
提供的String
文本将被显示。
当使用toEventually()
时,请注意不要进行状态变更或运行大量计算密集型代码,因为这个闭包会被多次运行。
编写你自己的匹配器
在Nimble中,匹配器是Swift函数,接收到一个预期值并返回一个Predicate
闭包。以equal
为例
// Swift
public func equal<T: Equatable>(expectedValue: T?) -> Predicate<T> {
// Can be shortened to:
// Predicate { actual in ... }
//
// But shown with types here for clarity.
return Predicate { (actualExpression: Expression<T>) throws -> PredicateResult in
let msg = ExpectationMessage.expectedActualValueTo("equal <\(expectedValue)>")
if let actualValue = try actualExpression.evaluate() {
return PredicateResult(
bool: actualValue == expectedValue!,
message: msg
)
} else {
return PredicateResult(
status: .fail,
message: msg.appendedBeNilHint()
)
}
}
}
Predicate
闭包的返回值是一个PredicateResult
,它指示实际值是否匹配预期值以及失败时的错误信息。
实际的
equal
匹配器函数在expected
为nil时不匹配;上面的示例已被编辑以节省空间。
由于匹配器只是Swift函数,您可以定义它们在任何地方:在测试文件顶部、所有测试共用的文件中,或您分发给其他人的Xcode项目中。
如果您编写了一个大家都可以使用的匹配器,请考虑通过发送pull request将其添加到Nimble的内置匹配器集中!或者您可以通过GitHub自行分发。
有关如何编写自己的匹配器的示例,请查看Matchers
目录,了解Nimble内置匹配器的实现方式。您还可以查看下面的提示。
PredicateResult
PredicateResult
是Predicate
返回的结构体,用于指示成功和失败。一个PredicateResult
由两个值组成:PredicateStatus
和ExpectationMessage
。
与布尔值不同,PredicateStatus
捕获一个三元组值集
// Swift
public enum PredicateStatus {
// The predicate "passes" with the given expression
// eg - expect(1).to(equal(1))
case matches
// The predicate "fails" with the given expression
// eg - expect(1).toNot(equal(1))
case doesNotMatch
// The predicate never "passes" with the given expression, even if negated
// eg - expect(nil as Int?).toNot(equal(1))
case fail
// ...
}
同时,ExpectationMessage
为错误报告提供了消息语义。
// Swift
public indirect enum ExpectationMessage {
// Emits standard error message:
// eg - "expected to <string>, got <actual>"
case expectedActualValueTo(/* message: */ String)
// Allows any free-form message
// eg - "<string>"
case fail(/* message: */ String)
// ...
}
在报告错误时,谓词通常应依赖于 .expectedActualValueTo(..)
或 .fail(..)
。其他枚举情况可以考虑特殊情况。
最后,如果您的谓词使用了其他谓词,可以使用 .appended(details:)
和 .appended(message:)
方法在现有错误上添加更多详细信息。
常用的附加信息是针对空值的失败。为此,可以使用 .appendedBeNilHint()
。
懒加载评估
actualExpression
是一个懒、记忆化闭包,它围绕着传递给 expect
函数的值。该表达式可以是闭包,或者直接传递给 expect(...)
的值。为了确定该值是否匹配,自定义匹配器应调用 actualExpression.evaluate()
// Swift
public func beNil<T>() -> Predicate<T> {
// Predicate.simpleNilable(..) automatically generates ExpectationMessage for
// us based on the string we provide to it. Also, the 'Nilable' postfix indicates
// that this Predicate supports matching against nil actualExpressions, instead of
// always resulting in a PredicateStatus.fail result -- which is true for
// Predicate.simple(..)
return Predicate.simpleNilable("be nil") { actualExpression in
let actualValue = try actualExpression.evaluate()
return PredicateStatus(bool: actualValue == nil)
}
}
在上面的示例中,actualExpression
不是 nil
- 它是一个返回值的闭包。通过 evaluate()
方法访问它返回的值,该值可能是 nil
。如果该值是 nil
,则 beNil
匹配器函数返回 true
,指示期望通过。
通过 Swift 泛型进行类型检查
使用 Swift 的泛型,匹配器可以通过修改返回类型来限制传递给 expect
函数的实际值的类型。
例如,以下匹配器 haveDescription
只接受实现了 Printable
协议的实际值。它检查其 description
是否与传递给匹配器函数的值相同,并在它们相同时通过。
// Swift
public func haveDescription(description: String) -> Predicate<Printable?> {
return Predicate.simple("have description") { actual in
return PredicateStatus(bool: actual.evaluate().description == description)
}
}
自定义失败消息
当使用 Predicate.simple(..)
或 Predicate.simpleNilable(..)
时,Nimble在期望失败时输出以下失败消息:
// where `message` is the first string argument and
// `actual` is the actual value received in `expect(..)`
"expected to \(message), got <\(actual)>"
您可以通过修改创建 Predicate
的方式来自定义此消息。
基本自定义
对于稍微复杂的错误消息,通过 Predicate.define(..)
接收创建的失败消息。
// Swift
public func equal<T: Equatable>(_ expectedValue: T?) -> Predicate<T> {
return Predicate.define("equal <\(stringify(expectedValue))>") { actualExpression, msg in
let actualValue = try actualExpression.evaluate()
let matches = actualValue == expectedValue && expectedValue != nil
if expectedValue == nil || actualValue == nil {
if expectedValue == nil && actualValue != nil {
return PredicateResult(
status: .fail,
message: msg.appendedBeNilHint()
)
}
return PredicateResult(status: .fail, message: msg)
}
return PredicateResult(bool: matches, message: msg)
}
}
在上面的示例中,msg
基于传递给 Predicate.define
的字符串定义。代码看起来像这样
// Swift
let msg = ExpectationMessage.expectedActualValueTo("equal <\(stringify(expectedValue))>")
完全自定义
要完全自定义Predicate的行为,请使用期望返回 PredicateResult
的重载。
除了 PredicateResult
之外,还有一些其他可以使用的 ExpectationMessage
枚举值。
public indirect enum ExpectationMessage {
// Emits standard error message:
// eg - "expected to <message>, got <actual>"
case expectedActualValueTo(/* message: */ String)
// Allows any free-form message
// eg - "<message>"
case fail(/* message: */ String)
// Emits standard error message with a custom actual value instead of the default.
// eg - "expected to <message>, got <actual>"
case expectedCustomValueTo(/* message: */ String, /* actual: */ String)
// Emits standard error message without mentioning the actual value
// eg - "expected to <message>"
case expectedTo(/* message: */ String)
// ...
}
对于组合其他匹配器的匹配器,有一系列辅助函数可以用来注释消息。
appended(message: String)
用于将消息附加到原始失败消息。
// produces "expected to be true, got <actual> (use beFalse() for inverse)"
// appended message do show up inline in Xcode.
.expectedActualValueTo("be true").appended(message: " (use beFalse() for inverse)")
对于跨越多行的更全面的消息,请使用 appended(details: String)
代替。
// produces "expected to be true, got <actual>\n\nuse beFalse() for inverse\nor use beNil()"
// details do not show inline in Xcode, but do show up in test logs.
.expectedActualValueTo("be true").appended(details: "use beFalse() for inverse\nor use beNil()")
异步Predicate
要针对异步表达式编写Predicate,返回一个 AsyncPredicate
实例。传递给 AsyncPredicate
的闭包是异步的,而您评估的表达式也是异步的,需要在使用时进行等待。
// Swift
actor CallRecorder<Arguments> {
private(set) var calls: [Arguments] = []
func record(call: Arguments) {
calls.append(call)
}
}
func beCalled<Argument: Equatable>(with arguments: Argument) -> AsyncPredicate<CallRecorder<Argument>> {
AsyncPredicate { (expression: AsyncExpression<CallRecorder<Argument>>) in
let message = ExpectationMessage.expectedActualValueTo("be called with \(arguments)")
guard let calls = try await expression.evaluate()?.calls else {
return PredicateResult(status: .fail, message: message.appendedBeNilHint())
}
return PredicateResult(bool: calls.contains(args), message: message.appended(details: "called with \(calls)"))
}
}
在这个例子中,我们创建了一个actor作为对象来记录对异步函数的调用。然后,我们创建了一个 beCalled(with:)
匹配器来检查actor是否收到了给定参数的调用。
支持 Objective-C
要从 Objective-C 中使用用 Swift 编写的自定义匹配器,您必须扩展 NMBPredicate
类,添加一个用于自定义匹配器的新类方法。下面示例定义了类方法 +[NMBPredicate beNilMatcher]
// Swift
extension NMBPredicate {
@objc public class func beNilMatcher() -> NMBPredicate {
return NMBPredicate { actualExpression in
return try beNil().satisfies(actualExpression).toObjectiveC()
}
}
}
这使您可以从 Objective-C 中使用匹配器
// Objective-C
expect(actual).to([NMBPredicate beNilMatcher]());
为使语法更容易使用,定义一个调用类方法的 C 函数
// Objective-C
FOUNDATION_EXPORT NMBPredicate *beNil() {
return [NMBPredicate beNilMatcher];
}
nil
在 Objective-C 匹配器中正确处理 当支持 Objective-C 时,请确保您适当地处理 nil
。就像 Cedar 一样,大多数匹配器都不会匹配 nil
。这是为了防止测试编写者对没有预期的 nil
值感到意外。
Nimble 为希望对 nil
对象创建期望的测试编写者提供了 beNil
匹配器函数
// Objective-C
expect(nil).to(equal(nil)); // fails
expect(nil).to(beNil()); // passes
如果您的匹配器不想与 nil
匹配,请使用 Predicate.define
或 Predicate.simple
。使用这些工厂方法会在它们为 nil
时自动生成预期的值失败消息。
public func beginWith<S: Sequence>(_ startingElement: S.Element) -> Predicate<S> where S.Element: Equatable {
return Predicate.simple("begin with <\(startingElement)>") { actualExpression in
guard let actualValue = try actualExpression.evaluate() else { return .fail }
var actualGenerator = actualValue.makeIterator()
return PredicateStatus(bool: actualGenerator.next() == startingElement)
}
}
extension NMBPredicate {
@objc public class func beginWithMatcher(_ expected: Any) -> NMBPredicate {
return NMBPredicate { actualExpression in
let actual = try actualExpression.evaluate()
let expr = actualExpression.cast { $0 as? NMBOrderedCollection }
return try beginWith(expected).satisfies(expr).toObjectiveC()
}
}
}
安装 Nimble
Nimble 可以独立使用,也可以与姊妹项目 Quick 一起使用。要安装 Quick 和 Nimble,请遵循 Quick 文档中的安装说明。
Nimble 可以通过两种方式之一安装:使用 CocoaPods,或者使用 git 子模块。
将 Nimble 安装为子模块
要将 Nimble 作为子模块用于测试 macOS、iOS 或 tvOS 应用程序,请按照以下 4 个简单步骤操作
- 克隆 Nimble 仓库
- 将 Nimble.xcodeproj 添加到您项目的 Xcode 工作空间中
- 将 Nimble.framework 链接到您的测试目标
- 开始编写预期!
有关每个步骤的更详细说明,请阅读 如何安装 Quick。忽略涉及将 Quick 添加到项目的步骤,以便仅安装 Nimble。
通过 CocoaPods 安装 Nimble
要将 Nimble 集成到 CocoaPods 中,并对 macOS、iOS、tvOS 或 watchOS 应用程序进行测试,请在 podfile 中添加 Nimble,并添加 use_frameworks!
行以启用 CocoaPods 的 Swift 支持。
platform :ios, '8.0'
source 'https://github.com/CocoaPods/Specs.git'
# Whatever pods you need for your app go here
target 'YOUR_APP_NAME_HERE_Tests', :exclusive => true do
use_frameworks!
pod 'Nimble'
end
最后运行 pod install
。
通过 Swift Package Manager 安装 Nimble
Xcode
要使用 Xcode 的 Swift 包管理器集成安装 Nimble,请选择项目配置,然后选择项目标签,然后选择“包依赖项”标签。单击列表底部的“+”按钮,然后按照向导将 Quick 添加到您的项目中。指定 https://github.com/Quick/Nimble.git
作为 URL,并确保将 Nimble 添加为单元测试目标的依赖项,而不是您的应用程序目标的依赖项。
Package.Swift
要使用 Swift 包管理器将 Nimble 与您的应用程序一起测试,请将 Nimble 添加到您的 Package.Swift
,并将其链接到您的测试目标。
// swift-tools-version:5.5
import PackageDescription
let package = Package(
name: "MyAwesomeLibrary",
products: [
// ...
],
dependencies: [
// ...
.package(url: "https://github.com/Quick/Nimble.git", from: "12.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "MyAwesomeLibrary",
dependencies: ...),
.testTarget(
name: "MyAwesomeLibraryTests",
dependencies: ["MyAwesomeLibrary", "Nimble"]),
]
)
请注意,如果您使用 Swift 包管理器安装 Nimble,则 raiseException
不可用。
在不使用 XCTest 的情况下使用 Nimble
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()
- 添加一个后构建操作以解决 Swift XCTest 支持库不必要地复制到您的应用程序中的问题
- 在 Xcode 中编辑您的方案,导航到“构建”-"后操作”
- 单击“+”图标并选择“新建运行脚本操作”
- 打开“提供构建设置来源”下拉菜单并选择您的目标
- 输入以下脚本内容
rm "${SWIFT_STDLIB_TOOL_DESTINATION_DIR}/libswiftXCTest.dylib"
您现在可以在您的代码中使用 Nimble 断言,并按需处理失败。