Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support Swift Testing #19

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 22 additions & 19 deletions Source/ViewControllerPresentationSpy/AlertVerifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import UIKit
import XCTest
import Testing

/**
Captures presented UIAlertControllers.
Expand Down Expand Up @@ -49,7 +50,7 @@ public class AlertVerifier: NSObject {
@objc override public init() {
super.init()
guard !AlertVerifier.isSwizzled else {
XCTFail("""
fail("""
More than one instance of AlertVerifier exists. This may be caused by \
creating one setUp() but failing to set the property to nil in tearDown().
""")
Expand Down Expand Up @@ -136,36 +137,37 @@ extension AlertVerifier {
preferredStyle: UIAlertController.Style = .alert,
presentingViewController: UIViewController? = nil,
file: StaticString = #filePath,
line: UInt = #line
line: UInt = #line,
sourceLocation: SourceLocation = #_sourceLocation
) {
let abort = verifyCalledOnce(actual: presentedCount, action: "present", file: file, line: line)
let abort = verifyCalledOnce(actual: presentedCount, action: "present", file: file, line: line, sourceLocation: sourceLocation)
if abort { return }
XCTAssertEqual(self.title, title, "alert title", file: file, line: line)
XCTAssertEqual(self.message, message, "alert message", file: file, line: line)
verifyAnimated(actual: self.animated, expected: animated, action: "present", file: file, line: line)
verifyActions(expected: actions, file: file, line: line)
verifyPreferredStyle(expected: preferredStyle, file: file, line: line)
assertEqual(self.title, title, "alert title", file: file, line: line, sourceLocation: sourceLocation)
assertEqual(self.message, message, "alert message", file: file, line: line, sourceLocation: sourceLocation)
verifyAnimated(actual: self.animated, expected: animated, action: "present", file: file, line: line, sourceLocation: sourceLocation)
verifyActions(expected: actions, file: file, line: line, sourceLocation: sourceLocation)
verifyPreferredStyle(expected: preferredStyle, file: file, line: line, sourceLocation: sourceLocation)
verifyViewController(actual: self.presentingViewController, expected: presentingViewController,
adjective: "presenting", file: file, line: line)
adjective: "presenting", file: file, line: line, sourceLocation: sourceLocation)
}

private func verifyActions(expected: [Action], file: StaticString, line: UInt) {
private func verifyActions(expected: [Action], file: StaticString, line: UInt, sourceLocation: SourceLocation) {
let actual = actionsAsSwiftType()
let minCount = min(actual.count, expected.count)
for i in 0 ..< minCount {
if actual[i] != expected[i] {
XCTFail("Action \(i): Expected \(expected[i]), but was \(actual[i])",
file: file, line: line)
fail("Action \(i): Expected \(expected[i]), but was \(actual[i])",
file: file, line: line, sourceLocation: sourceLocation)
}
}
if actual.count < expected.count {
let missing = expected[actual.count ..< expected.count].map { $0.description }
XCTFail("Did not meet count of \(expected.count) actions, missing \(missing.joined(separator: ", "))",
file: file, line: line)
fail("Did not meet count of \(expected.count) actions, missing \(missing.joined(separator: ", "))",
file: file, line: line, sourceLocation: sourceLocation)
} else if actual.count > expected.count {
let extra = actual[expected.count ..< actual.count].map { $0.description }
XCTFail("Exceeded count of \(expected.count) actions, with unexpected \(extra.joined(separator: ", "))",
file: file, line: line)
fail("Exceeded count of \(expected.count) actions, with unexpected \(extra.joined(separator: ", "))",
file: file, line: line, sourceLocation: sourceLocation)
}
}

Expand All @@ -186,15 +188,16 @@ extension AlertVerifier {

private func verifyPreferredStyle(expected: UIAlertController.Style,
file: StaticString,
line: UInt)
line: UInt,
sourceLocation: SourceLocation)
{
let actual = preferredStyle
if actual != expected {
switch expected {
case .actionSheet:
XCTFail("Expected preferred style .actionSheet, but was .alert", file: file, line: line)
fail("Expected preferred style .actionSheet, but was .alert", file: file, line: line, sourceLocation: sourceLocation)
case .alert:
XCTFail("Expected preferred style .alert, but was .actionSheet", file: file, line: line)
fail("Expected preferred style .alert, but was .actionSheet", file: file, line: line, sourceLocation: sourceLocation)
@unknown default:
fatalError("Unknown UIAlertController.Style for preferred style")
}
Expand Down
12 changes: 7 additions & 5 deletions Source/ViewControllerPresentationSpy/DismissalVerifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import UIKit
import XCTest
import Testing

/**
Captures dismissed view controllers.
Expand Down Expand Up @@ -38,7 +39,7 @@ public class DismissalVerifier: NSObject {
@objc override public init() {
super.init()
guard !DismissalVerifier.isSwizzled else {
XCTFail("""
fail("""
More than one instance of DismissalVerifier exists. This may be caused by \
creating one setUp() but failing to set the property to nil in tearDown().
""")
Expand Down Expand Up @@ -87,12 +88,13 @@ public extension DismissalVerifier {
animated: Bool,
dismissedViewController: UIViewController? = nil,
file: StaticString = #file,
line: UInt = #line
line: UInt = #line,
sourceLocation: SourceLocation = #_sourceLocation
) {
let abort = verifyCalledOnce(actual: dismissedCount, action: "dismiss", file: file, line: line)
let abort = verifyCalledOnce(actual: dismissedCount, action: "dismiss", file: file, line: line, sourceLocation: sourceLocation)
if abort { return }
verifyAnimated(actual: self.animated, expected: animated, action: "dismiss", file: file, line: line)
verifyAnimated(actual: self.animated, expected: animated, action: "dismiss", file: file, line: line, sourceLocation: sourceLocation)
verifyViewController(actual: self.dismissedViewController, expected: dismissedViewController,
adjective: "dismissed", file: file, line: line)
adjective: "dismissed", file: file, line: line, sourceLocation: sourceLocation)
}
}
18 changes: 10 additions & 8 deletions Source/ViewControllerPresentationSpy/PresentationVerifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import UIKit
import XCTest
import Testing

/**
Captures presented view controllers.
Expand Down Expand Up @@ -41,14 +42,14 @@ public class PresentationVerifier: NSObject {
@objc override public init() {
super.init()
guard !PresentationVerifier.isSwizzled else {
XCTFail("""
fail("""
More than one instance of PresentationVerifier exists. This may be caused by \
creating one setUp() but failing to set the property to nil in tearDown().
""")
return
}
guard !AlertVerifier.isSwizzled else {
XCTFail("""
fail("""
A PresentationVerifier may not be created while an AlertVerifier exists. Try \
making the AlertVerifier optional, and setting it to nil before creating the \
PresentationVerifier.
Expand Down Expand Up @@ -99,17 +100,18 @@ public extension PresentationVerifier {
animated: Bool,
presentingViewController: UIViewController? = nil,
file: StaticString = #filePath,
line: UInt = #line
line: UInt = #line,
sourceLocation: SourceLocation = #_sourceLocation
) -> VC? {
let abort = verifyCalledOnce(actual: presentedCount, action: "present", file: file, line: line)
let abort = verifyCalledOnce(actual: presentedCount, action: "present", file: file, line: line, sourceLocation: sourceLocation)
if abort { return nil }
verifyAnimated(actual: self.animated, expected: animated, action: "present", file: file, line: line)
verifyAnimated(actual: self.animated, expected: animated, action: "present", file: file, line: line, sourceLocation: sourceLocation)
verifyViewController(actual: self.presentingViewController, expected: presentingViewController,
adjective: "presenting", file: file, line: line)
adjective: "presenting", file: file, line: line, sourceLocation: sourceLocation)
let nextVC = presentedViewController as? VC
if nextVC == nil {
XCTFail("Expected presented view controller to be \(VC.self)), " +
"but was \(String(describing: presentedViewController))", file: file, line: line)
fail("Expected presented view controller to be \(VC.self)), but was \(String(describing: presentedViewController))",
file: file, line: line, sourceLocation: sourceLocation)
}
return nextVC
}
Expand Down
49 changes: 40 additions & 9 deletions Source/ViewControllerPresentationSpy/VerifyHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,25 @@

import UIKit
import XCTest
import Testing

func verifyCalledOnce(actual: Int, action: String, file: StaticString, line: UInt) -> Bool {
func verifyCalledOnce(actual: Int, action: String, file: StaticString, line: UInt, sourceLocation: SourceLocation) -> Bool {
if actual == 0 {
XCTFail("\(action) not called", file: file, line: line)
fail("\(action) not called", file: file, line: line, sourceLocation: sourceLocation)
return true // Abort test
}
if actual > 1 {
XCTFail("\(action) called \(actual) times", file: file, line: line)
fail("\(action) called \(actual) times", file: file, line: line, sourceLocation: sourceLocation)
}
return false // Continue test
}

func verifyAnimated(actual: Bool, expected: Bool, action: String, file: StaticString, line: UInt) {
func verifyAnimated(actual: Bool, expected: Bool, action: String, file: StaticString, line: UInt, sourceLocation: SourceLocation) {
if actual != expected {
if expected {
XCTFail("Expected animated \(action), but was not animated", file: file, line: line)
fail("Expected animated \(action), but was not animated", file: file, line: line, sourceLocation: sourceLocation)
} else {
XCTFail("Expected non-animated \(action), but was animated", file: file, line: line)
fail("Expected non-animated \(action), but was animated", file: file, line: line, sourceLocation: sourceLocation)
}
}
}
Expand All @@ -30,12 +31,42 @@ func verifyViewController(actual: UIViewController?,
expected: UIViewController?,
adjective: String,
file: StaticString,
line: UInt) {
line: UInt,
sourceLocation: SourceLocation) {
if let expected = expected, let actual = actual {
XCTAssertTrue(
assertTrue(
expected === actual,
"Expected \(adjective) view controller to be \(expected)), but was \(actual)",
file: file,
line: line)
line: line,
sourceLocation: sourceLocation)
}
}

func fail(_ comment: Comment,
file: StaticString = #file,
line: UInt = #line,
sourceLocation: SourceLocation = #_sourceLocation) {
XCTFail(comment.rawValue, file: file, line: line)
Issue.record(comment, sourceLocation: sourceLocation)
}

func assertEqual<T>(_ expression1: @autoclosure () -> T,
_ expression2: @autoclosure () -> T,
_ comment: Comment? = nil,
file: StaticString = #filePath,
line: UInt = #line,
sourceLocation: SourceLocation = #_sourceLocation) where T : Equatable {
XCTAssertEqual(expression1(), expression2(), comment?.rawValue ?? "", file: file, line: line)
#expect(expression1() == expression2(), comment, sourceLocation: sourceLocation)
}


func assertTrue(_ expression: @autoclosure () -> Bool,
_ comment: Comment? = nil,
file: StaticString = #filePath,
line: UInt = #line,
sourceLocation: SourceLocation = #_sourceLocation) {
XCTAssertTrue(expression(), comment?.rawValue ?? "", file: file, line: line)
#expect(expression(), comment, sourceLocation: sourceLocation)
}