📝 面试求职: ,内容涵盖 测试基础、Linux操作系统、MySQL数据库、Web功能测试、接口测试、APPium移动端测试、Python知识、Selenium自动化测试相关、性能测试、性能测试、计算机网络知识、Jmeter、HR面试,命中率杠杠的。(大家刷起来…)
📝 职场经验干货:
大家好!我叫 Cyan,是 Mercoin iOS 团队的成员之一,我想分享一些关于 Swift 测试的经验。
个人认为 Swift Testing 比XCTest更加容易使用,功能也更加完善。
Swift Testing 是 Apple 在今年的WWDC24上推出的全新单元测试框架。它旨在成为广为使用的 XCTest 框架的后继者。Swift Testing 只能从 Xcode 16 开始使用,因此如果您的团队尚未更新项目,那么现在是时候更新了 🙂
属性和宏(Attributes and Macros)
@test
当我们使用XCTest时,我们会test在函数名的开头添加,以将该函数作为测试用例。
import XCTest
func test_defaultValue() {
// ...
}
但是对于 Swift 测试,我们不需要添加test而是使用@Test属性。
import Testing
@Test func defaultValue() {
// ...
}
与 XCTest 的测试功能相同,我们仍然可以在测试中添加async、throws和@MainActor。
#expect
此宏用于实际执行检查。它与 XCTest 的XCAssert函数相同。不过,使用 XCTest 进行 Swift 测试的一个关键区别是,我们不需要针对检查的不同情况使用特定的函数。
对于 XCTest,我们可以使用以下所有这些函数:
XCTAssert, XCTAssertTrue, XCTAssertFalse
XCTAssertNil, XCTAssertNotNil
XCTAssertEqual, XCTAssertNotEqual
XCTAssertIdentical, XCTAssertNotIdentical
XCTAssertGreaterThan, XCTAssertGreaterThanOrEqual
XCTAssertLessThan, XCTAssertLessThanOrEqual
然而,在 Swift 测试中,你可以像这样进行操作:
#expect(amount == 5000)
#expect(user.name == "Hoge")
#expect(!array.isEmpty)
#expect(numbers.contains(1))
#expect(paymentAmount > 0)
我们只需要传递一个表达式给#expect,这样更简单,也更容易记住。
#require
当你想要有一个必需的期望时,可以使用此宏。这意味着,当此测试用例失败时,整个测试将停止并失败。
try #require(date.isValid) // ← if it fails here...
#expect(date, Date(timeIntervalSince1970: 0)) // ← then this is not executed
此外,当你想要解开可选值时也可以使用它,并在所述可选值为时停止测试nil。
let method = try #require(paymentMethods.first) // ← if .first is nil...
#expect(paymentMethods.isCreditCard) // ← then this is not executed
特质(Traits)
这些是 Swift Testing 中的新功能,它们提供了更简单的方法来自定义我们的单元测试。引入了许多特征,因此我尝试将它们分为 3 类,以便于记忆:
细节相关特质
显示名称
此 trait 允许我们为测试用例添加名称。当然,我们可以从函数名称中知道测试用例的作用,但如果我们使用此 Display Name trait,则可以更容易理解它,因为我们可以在其上添加空格。
@Test("Check default value when there’s a plan")
func defaultValueWithPlan() {
let dependency = Dependency(plan: 1000)
#expect(selectedAmount == 1000)
}
Trait .bug
如果在修复特定错误后添加了所述测试用例,则此特性允许我们链接问题。
@Test(.bug("example.com/issues/123", "Check default value when there’s no plan")func defaultValueWithNoPlan() throws {
…
let firstAmountOption = try #require(amounts.first)
#expect(selectedAmount == firstAmountOption)
}
Trait .tags
此特性允许我们向测试用例添加标签,并能够在 Xcode 的左侧面板上看到它,以便更轻松地组织测试用例。首先,我们必须有一个 Tag 扩展来添加我们想要的标签。
extension Tag {
@Tag static var formatting: Self
@Tag static var location: Self
@Tag static var playback: Self
@Tag static var reviews: Self
@Tag static var users: Self
}
然后,你可以像这样使用它:
struct SwiftTestingDemoTests { @Test(.tags(.formatting)) func rating() async throws {
// add #expect here
}
…
@Test(.tags(.location)) func getLocation() async throws {
// add #expect here
}
…
@Test(.tags(.reviews)) func addReviews() async throws {
// add #expect here
}
}
你会看到类似这样的内容:
你可以将测试分组到一个套件中,然后在该套件上添加标签。这会将标签添加到该套件内的所有测试中。
@Suite(.tags(.defaultValue)) // ← add .tags herestruct SelectedAmountDefaultValue {
@Test func defaultValueWithPlan() async throws {
…
}
@Test func defaultValueWithNoPlan() async throws {
…
}
}
我稍后会分享更多关于 Suites 的信息 🙇
条件相关特征
Trait .enabled
此特性允许我们指定是否要运行测试用例的条件。
@Test(.enabled(if: FeatureFlag.isAccordionEnabled))func defaultValueAccordionState() {
// ...
}
Trait.disabled
此特性允许我们无条件禁用测试。当您的项目中存在不稳定的测试并导致延迟时,这可能会很有用。
@Test(.disabled("Due to flakiness"))func flakyTestExample() {
// ...
}
Trait @available
此特性允许我们添加一个条件,根据操作系统版本来决定是否运行测试。
@Test@available(macOS 15, *)
func caseForFunctionThatUsesNewAPIs() {
// ...
}
提示:Apple 建议使用@available而不是在运行时检查#available。
// ✖︎ Avoid checking availability at runtime using #available@Test func caseForFunctionThatUsesNewAPIs() {
guard #available(macOS 15, *) else { return }
// ...
}
// ⚪︎ Prefer @available attribute on test function
@Test
@available(macOS 15, *)
func caseForFunctionThatUsesNewAPIs() {
// ...
}
行为相关特征
Trait .timeLimit
此特性允许我们为测试用例添加时间。如果你不希望某个函数的运行时间超过某个时间阈值,则此特性非常有用。
@Test(.timeLimit(.minutes(5)))func someMethod() {
// ...
}
Trait .serialized
此特性允许我们按顺序运行套件中的测试,而不是同时运行所有测试。
@Suite(.serialized)struct SelectedAmountDefaultValue {
@Test func defaultValueWithPlan() {
...
}
@Test func defaultValueWithNoPlan() {
...
}
}
现在我们已经讨论了特征,让我们继续讨论一些可以在 Swift 测试中使用的技巧和窍门。
配对特质(Pairing Traits)
你还可以在一个测试用例中使用多个特征。
@Test( .disabled("Due to a crash"),
.bug("example.org/bugs/123", "Crashes at <symbol>")
)
func testExample() {
// ...
}
套件(Suites)
你可能已经注意到,本文多次提到了套件。基本上,套件是一组测试功能。
使用@Suite注释。
@Suite(.tags(.defaultValue))struct SelectedAmountDefaultValueNilPlanTests {
let dependency = Dependency(plan: nil)
init() throws {
...
}
deinit {
...
}
@Test("Check when there’s initial amount")
func withInitialAmount() {
// #expect…
}
@Test("Check when there’s no initial amount")
func withNoInitialAmount() {
// #expect…
}
}
参数化测试
当你有一些重复测试时,你可以使用参数化@Test函数。重复测试的一个示例如下:
// ✖︎ not recommendedstruct CryptoCurrencyTests {
@Test func includesBTC() async throws {
let data = try await GetData()
let currency = try #require(data.first(where: { $0 == "BTC" } ))
#expect(currency == “BTC”)
}
@Test func includesETH() async throws {
let data = try await GetData()
let currency = try #require(data.first(where: { $0 == "ETH" } ))
#expect(currency == “ETH”)
}
// ...and more, similar test functions
}
当然,你可以使用for…in循环来重复测试,但不建议这样做。
// ✖︎ also not recommended - using a for…in loop to repeat a test@Test func includesCryptoNames() async throws {
let cryptoNames = [
"BTC",
"ETH",
"CryptoA",
"CryptoB",
]
let data = try await GetData()
for cryptoName in cryptoNames {
let currency = try #require(data.first(where: { $0 == cryptoName } ))
#expect(currency == cryptoName)
}
}
让我们尝试使用参数化测试函数!将其更改为参数化@Test函数将是这样的:
// ⚪︎ recommendedstruct CryptoCurrencyTests {
@Test("Check master contains the correct cryptos", arguments: [
"BTC",
"ETH",
"CryptoA",
"CryptoB",
])
func includes(cryptoName: String) async throws {
let data = try await GetData()
let currency = try #require(data.first(where: { $0 == cryptoName } ))
#expect(currency == cryptoName)
}
}
通过命令行运行Swift Testing
就像 XCTest 一样,我们也可以在命令行中使用 Swift Testing,以便它可以在具有 CI/CD 的项目中使用。请使用以下命令:
从 XCTest 迁移
实际上,我们可以将 Swift Testing 与 XCTests 一起使用。当我们有类似的 XCTests 时,我们可以将它们合并为一个参数化@Test函数。最后,test从测试用例的名称中删除。
结论
就我个人而言,我更喜欢 Swift Testing,而不是 XCTest。与 XCTest 相比,Swift Testing 改进了很多,并且比以前更容易创建单元测试。Swift Testing 只能从 Xcode 16 开始使用,因此如果你尚未更新项目以使用 Xcode 16,你可能需要等待一段时间才能开始使用 Swift Testing。
最后: 下方这份完整的软件测试视频教程已经整理上传完成,需要的朋友们可以自行领取【保证100%免费】