Swift             License: Unlicense

PlaygroundTester

PlaygroundTester 是一个软件包,使您能够向 iPad Swift Playgrounds 项目添加测试。

安装

只需像往常一样将 PlaygroundTester 软件包添加到项目中即可。

用法

添加测试

为了让 PlaygroundTester 找到并正确执行测试,您的测试类需要:

  1. 继承自 TestCase
  2. 标记为具有 @objcMembers
    1. 或者,您可以将您希望被发现的每个方法标记为 @objc

目前,**不支持**从另一个测试类继承(因此您不能创建一个 class BaseTests: TestCase,然后其他测试类从它继承)。

示例测试类声明

@objcMembers
final class MyTests: TestCase {
}

setUp / tearDown

您可以覆盖四个方法来帮助设置和清理测试。

  // Called once for the entire test class, before any tests are run.
  open class func setUp() {  }

  // Called before each test method.
  // If this method throws, the test method won't be executed, but `tearDown` will be.
  open func setUp() throws { }

  // Called after each test method.
  open func tearDown() throws { }

  // Called once for the entire test class, after all tests are run.
  open class func tearDown() { }
}

添加测试方法

为了让 PlaygroundTester 发现您的测试方法并自动运行它们,它们必须:

  1. private (即 publicinternal)
  2. test 开头

任何 private 方法或不以 test 开头的方法都不会被自动执行,因此您可以利用这个机会来定义辅助方法。

示例方法定义

func testSample() { }

// Methods that won't be run automaticaly
private func testPrivateSample() { }

func helperMethod() { }

抛出异常的测试方法

PlaygroundTester 支持抛出异常的测试方法 - 只需按照上述规则定义您的方法,并在其定义中添加 throwsPlaygroundTester 将捕获抛出的错误并报告它。

抛出异常的测试方法的示例定义

func testSampleThrowing() throws { }

断言

目前,有一组基本的断言方法可用,它们模仿了 XCTest 的断言风格

// Assert of a passed boolean value is `true`/`false`
public func Assert(_ value: Bool, message: String = "")
public func AssertFalse(_ value: Bool, message: String = "")

// Assert if two passed values are equal / not equal.
public func AssertEqual<T: Equatable>(_ value: T, other: T, message: String = "") 
public func AssertNotEqual<T: Equatable>(_ value: T, other: T, message: String = "")

// assert if passed optional value is `nil` / not `nil`.
public func AssertNil<T>(_ value: T?, message: String = "")
public func AssertNotNil<T>(_ value: T?, message: String = "")

XCTAssert 方法系列实现对等还有一些方法缺失,这些方法将在稍后添加。

解包

PlaygroundTester 提供了一个类似于 XCTUnwrap 的方法

// Return an unwrapped value, or throw an error if `nil` was passed.
public func AssertUnwrap<T>(_ value: T?, message: String = "") throws -> T

您应该用 throws 标记您的测试方法,以避免需要自己处理这个抛出的错误。

带有 AssertUnwrap 的示例方法

func testSampleAssertUnwrap() throws {

  let sampleArray = ["first", "second"]
  
  let firstValue = try XCTUnwrap(sampleArray.first, "Array should have a first element").

  // `firstValue` is non-optional here
}

期望

PlaygroundTester 支持等待期望来测试异步代码。 期望可以使用 3 个属性进行配置:

  1. expectedFulfilmentCount (默认 == 1) - 期望应该被满足多少次才被认为是已满足。 如果期望被过度满足,将会失败。
  2. inverted (默认 == false) - 如果期望是 inverted,则如果它被满足,将会失败。
    1. 如果您有一个 inverted 期望,且 expectedFulfilmentCount > 1,那么如果它被满足的次数少于 expectedFulfilmentCount,则会被认为是已满足。

您可以使用 AssertExpectations 方法来等待创建的期望

// Will wait for `timeout` seconds for `expectations` to be fulfilled before continuing test execution.
public func AssertExpectations(_ expectations: [Expectation], timeout: TimeInterval)

带有期望的示例测试

func testSampleExpectation() {
  let expectation = Expectation(name: "Wait for main thread")
  
  DispatchQueue.main.async { 
    expectation.fulfill()
  }
  
  AssertExpectations([expectation], timeout: 2)
}

目前,未等待的期望不会触发断言失败。

运行测试

为了执行您的测试,您需要做的最后一件事是:将您的视图包装在 PlaygroundTesterWrapperView 中,并将 PlaygroundTester.PlaygroundTesterConfiguration.isTesting 标志设置为 true。在您的 App 对象中,只需这样做:

struct Myapp: App {
    init() {
        PlaygroundTester.PlaygroundTesterConfiguration.isTesting = true
    }
    var body: some Scene {
        WindowGroup {
          PlaygroundTester.PlaygroundTesterWrapperView {
            // YourContentView()
          }
        }
    }
}

之后,当以全屏或预览模式运行应用程序时,将改为发现并运行您的测试。

检查结果

测试运行后,您可以导航它们以检查其结果并查看哪些断言失败。

pg_tester_sample.mp4

修补 Package.swift

Swift Playgrounds 目前不支持多个目标,因此您的测试文件需要与常规应用程序代码一起保存。 该软件包本身在其代码周围具有编译保护,因此在创建发布版本时,大部分代码将从您的生产应用程序中省略。 剩下的是对象和函数定义的最小集合,以便您的代码可以很好地编译,并且对 PlaygroundTester 提供的对象/函数的所有调用都基本上解析为 no-ops。

如果您还想从发布版本中删除您的测试,您需要在您的应用程序中添加一个编译标志。

现在,您需要按照以下步骤将编译标志添加到您的项目中:

  1. 将应用程序项目发送到 Mac(例如通过 AirDrop)
  2. 打开包内容(右键单击 -> 显示包内容)
  3. 打开 Package.swift 文件
  4. 该文件应包含单个 .executableTarget 定义。
  5. 将此参数添加到目标:swiftSettings: [.define("TESTING_ENABLED", .when(configuration: .debug))]
  6. 保存文件并将应用程序共享回您的 iPad。

最后,目标定义应类似于这样:

targets: [
        .executableTarget(
            name: "AppModule",
            dependencies: [
              // if any
            ],
            path: ".",
            swiftSettings: [.define("TESTING_ENABLED", .when(configuration: .debug))]
        )
    ]

您当然可以为该标志选择任何名称。

注意:我希望在 修补 Package.swift 中自动化此过程

支持的功能

路线图

我想探索并添加到 PlaygroundTester 的事项(随机顺序)

请查看 Issues 了解更多信息。