SSpec 是一个 Swift 的行为驱动开发 (BDD) 框架,它提供了一种很棒的替代标准 XCTest 的方案。
如果你之前使用过行为驱动开发 (BDD),那么 SSpec 对你来说应该很熟悉。 对于那些从未使用过任何 BDD 框架的人来说,它也很容易理解和上手。
在你的项目的 Package.swift
文件中添加 SSpec 作为包依赖项
.package(url: "https://github.com/dimakura/SSpec", from: "0.2.3")
你还应该将其放在测试目标的依赖项下
.testTarget(
name: "MyPackageTests",
dependencies: [
"SSpec",
// other dependencies
]),
包管理器会在构建期间为你下载 SSpec
swift build
不要忘记导入 SSPec 才能使用它
import SSpec
通过查看这个简单的例子,你可以很容易地了解很多关于 SSpec 的信息
SSpec.run { // 1
it("2 + 2 = 4") { // 2
expect(2 + 2).to.eq(4) // 3
}
}
XCTAssert(SSpec.hasErrors == false)
运行 SSpec 的标准方法是使用 SSpec.run
方法,就像我们在示例的第一行所做的那样。 在此方法的闭包中,我们定义了我们的测试用例。
第一个(也是唯一的)测试用例在第二行定义。 它有一个标题 ("2 + 2 = 4"
) 和另一个闭包。
在第三行测试用例的闭包中,你可以看到这个例子实际上试图测试什么。 它确保 2 + 2
确实是 4
。
你可以在 SSpec.run {...}
的主体中放置任意数量的用例。 你通常应该创建一个单独的 SSpec.run {...}
并在其中定义所有测试。 SSpec 运行所有用例并检查它们的正确性。
如果任何用例失败,SSpec 将打印详细的错误报告,以便你可以轻松找到问题的根源。
带有默认 SSpec 报告器的示例输出如下所示
要以编程方式检测失败,你可以使用 SSpec.hasErrors
属性,该属性默认为 false
,但一旦你的规范中至少有一个用例失败,它就会变为 true
。
你可以使用此属性将失败报告回 XCTest
XCTAssert(SSpec.hasErrors == false)
这不是绝对必要的。 但是,当你的 CI 依赖于 XCTest 的失败时,这是一种检测错误的好方法。
两个全局函数 describe
和 context
有助于对相似的测试进行分组。 它们也有助于记录你的代码。
SSpec.run {
describe("Person") {
context("with high salary") {
let person = Person(salary: .High)
it("is rich") {
expect(person.isRich).to.beTrue
}
}
context("with low salary") {
let person = Person(salary: .Low)
it("is not rich") {
expect(person.isRich).to.beFalse
}
it("is poor") {
expect(person.isPoor).to.beTrue
}
}
}
}
你可以互换使用 describe
和 context
。 通常,程序员使用 describe
在高级别对示例进行分组,并使用 context
将它们分解为更详细的示例。
如果多个测试使用共享变量或需要相同的初始化代码,你可以方便地使用 before 钩子。 Before 钩子在每个测试之前运行。
如果你需要在运行每个测试后进行清理,你可以使用 after 钩子来完成此操作。
describe("Event") {
var event: Event!
before {
// This code runs before each example
event = Event(priority: .High, dueDate: Date.tomorrow())
event.save()
}
it("is urgent") {
expect(event.urgent).to.beTrue
}
it("is 1 day away") {
expect(event.daysLeft).to.eq(1)
}
after {
// This code runs after each example
Database.clean()
}
}
请注意,如果不同级别存在 before 钩子,则它们都会从顶层开始执行。 对于 after 钩子,执行从最低级别开始。
describe("Level 1") {
before {
// this runs first
}
describe("Level 2") {
before {
// this runs second
}
it("Example") {
// this runs after two before hooks
}
after {
// this runs first after the "Example"
}
}
after {
// this runs last
}
}
所有值都可以使用 beNil
匹配器来测试是否存在 (presence)
it("is nil") {
expect(value).to.beNil
}
要测试布尔值,请使用 beTrue
和 beFalse
匹配器
it("is true") {
expect(true).to.beTrue
}
it("is false") {
expect(false).to.beFalse
}
待办事项 (TODO)
待办事项 (TODO)
待办事项 (TODO)
待办事项 (TODO)
Swift 是一种强类型语言。 SSpec 使用类型来扩展匹配器。
假设你创建了自定义类 Person
class Person {
let name: String
let salary: Double
init(_ name: String, _ salary: Double) {
self.name = name
self.salary = salary
}
var isRich: Bool {
return salary > 50000
}
var isPoor: Bool {
return !isRich
}
}
并且希望你的规范像这样
describe("Person") {
let jack = Person("Jack", 100000)
let jane = Person("Jane", 20000)
describe(jack.name) {
it("is rich") {
expect(jack).to.beRich
}
}
describe(jane.name) {
it("is poor") {
expect(jane).to.bePoor
}
}
}
你所需要做的,就是扩展 SSExcept<T>
类
extension SSExpect where T == Person {
var beRich: Void {
// Note that `assert` takes into account negation status.
// If you need to know negation status use `isNegate` variable.
assert(
value!.isRich, // `value` is of type `Person?`
error: "Expected \(value!.name) to be rich", // There's also standard `valueStr` variable and
// `toString(value:)` function you can use.
errorNegate: "Expected \(value!.name) to be poor"
)
}
var bePoor: Void {
assert(
value!.isPoor,
error: "Expected \(value!.name) to be poor",
errorNegate: "Expected \(value!.name) to be rich"
)
}
}
默认情况下,SSpec 使用点来报告进度。
还有其他选项可用
SSpec.reporter = .Dot // default reporter
SSpec.reporter = .Spec // most document-like reporter
SSpec.reporter = .Progress // progress bar reporter
如果您有兴趣为 SSpec 做出贡献,请在此处阅读相关信息。