如何关闭 XCTestCase 中的警报

How to dismiss Alert in XCTestCase

class ProfileVC: BaseUIVC {
    var dispatchGroupHelper: DispatchGroupImpl
    var isFlag: Bool
    init(dispatchGroupHelper: DispatchGroupImpl = DispatchGroupHelper(), isFlag: Bool = false) {
        self.dispatchGroupHelper = dispatchGroupHelper
        self.isFlag = isFlag
        super.init(nibName: nil, bundle: currentBundle())
    }

    func save() {
        print("Outside isFlag : \(isFlag)")
        print("Outside self : \(self)")
        if let info = getData() {             
            showAlertOnWindow(message: "message") { (_) in
                print("Inside isFlag : \(self.isFlag)")
                print("Inside self : \(self)")
                if param.count > 0 {
                   self.dispatchGroupHelper.joinDispatchGroup()
                   self.requestAPI1(data: param)
                }
                if isFlag {
                    self.dispatchGroupHelper.joinDispatchGroup()
                    self.requestAPI2(data: param)
                }
                self.dispatchGroupHelper.notifyDispatchGroup(execute: {
                    self.handleResponse()
                })
            }
        }
    }

    public func showAlertOnWindow(title: String? = "", message: String? = nil,
                                  completionHandler: ((_ title: String) -> Void)? = nil) {
        let alert = UIAlertController(title: title ?? "", message: message,
                                      preferredStyle: UIAlertControllerStyle.alert)
        alert.addAction(UIAlertAction(title: "dummy", style: UIAlertActionStyle.default, handler: { (_) in
            if let completionHanlder = completionHandler {
                completionHanlder("OK")
            }
        }))
        alert.show()
        print("Display alert : \(alert)")
    }
}
class DispatchGroupHelper: DispatchGroupImpl {
    lazy var group: DispatchGroup? = {
        return DispatchGroup()
    }()
}
protocol DispatchGroupImpl {
    var group: DispatchGroup? { get }
    func joinDispatchGroup()
    func leaveDispatchGroup()
    func notifyDispatchGroup(execute: @escaping () -> Void)
}

extension DispatchGroupImpl {
    func joinDispatchGroup() {
        group?.enter()
    }

    func leaveDispatchGroup() {
        group?.leave()
    }

    func notifyDispatchGroup(execute: @escaping () -> Void) {
        group?.notify(queue: DispatchQueue.main, execute: execute)
    }
}

extension UIViewController {
    func show() {
        present(animated: true, completion: nil)
    }

    func present(animated: Bool, completion: (() -> Void)?) {
        if let rootVC = UIApplication.shared.keyWindow?.rootViewController {
            if #available(iOS 13.0, *), let navVC = rootVC as? UINavigationController,
                let visibleVC = navVC.visibleViewController {
                print("VisibleVC : \(visibleVC)")
                visibleVC.showPrompt(withAlertController: self)
            } else {
                rootVC.showPrompt(withAlertController: self)
            }
        }
    }
}

public extension UIAlertController {
        func tapButton(title: String) {
            guard let action = actions.first(where: {[=10=].title == title}), let block = action.value(forKey: "handler") else {
            return
        }
        let handler = unsafeBitCast(block as AnyObject, to: AlertHandler.self)
        handler(action)
    }
}

测试用例

class MockDispatchGroupImpl: DispatchGroupImpl {
    let group: DispatchGroup?
    var joinCount = 0
    var leaveCount = 0
    var isNotifyCalled = false
    init(group: DispatchGroup) {
        self.group = group
    }
    func joinDispatchGroup() {
        joinCount += 1
        print("Join: \(joinCount)")
    }
    func leaveDispatchGroup() {
        leaveCount += 1
        print("Leave")
    }
    func notifyDispatchGroup() {
        isNotifyCalled = true
        print("Notify")
    }
}

class ProfileTests: XCTestCase {
    func testAPIWhenIsFlagTrue() {
        var mockDispatchGroupHelper: MockDispatchGroupImpl? = MockDispatchGroupImpl(group: DispatchGroup())
        var profilerVC = getProfileVC(dispatchGroupHelper: mockDispatchGroupHelper!, isFlag: true)
        profilerVC?.save()
        let alert = UIApplication.shared.keyWindow?.rootViewController?.presentedViewController as! UIAlertController
        alert.tapButton(title: "OK")
        print("Capture Alert : \(alert)")
        print("TAP")
        XCTAssertEqual(mockDispatchGroupHelper?.joinCount, 2)
    }

    func testAPIWhenIsFlagFalse() {
        var mockDispatchGroupHelper: MockDispatchGroupImpl? = MockDispatchGroupImpl(group: DispatchGroup())
        var profilerVC = getProfileVC(dispatchGroupHelper: mockDispatchGroupHelper!, isFlag: false)
        profilerVC?.save()
        let alert = UIApplication.shared.keyWindow?.rootViewController?.presentedViewController as! UIAlertController
        alert.tapButton(title: "OK")
        print("Capture Alert : \(alert)")
        print("TAP")
        XCTAssertEqual(mockDispatchGroupHelper?.joinCount, 1)
    }
}

运行 个测试正在通过。但是当 ProfileTests
中的所有测试都是 运行 testAPIWhenIsFlagTrue:通过
testAPIWhenIsFlagFalse: 失败

"XCTAssertEqual failed: ("Optional(0)") is not equal to ("Optional(1)")"

调试结果

["testAPIWhenIsFlagTrue"]
Outside isFlag : true
Outside self: <ProfileVC: 0x7fdd13818600>
VisibleVC : <SplashViewController: 0x7fdd123023d0>
**Display alert : <UIAlertController: 0x7fdd1282a000>**    
**["Capture Alert : <UIAlertController: 0x7fdd1282a000>"]**
["TAP"]
Inside isFlag : true
Inside self: <ProfileVC: 0x7fdd13818600>
["Join: 1"]
["Join: 2"]
["Notify"]

["testAPIWhenIsFlagFalse"]
Outside isFlag : false
Outside self: <ProfileVC: 0x7fdd10242a00>
**VisibleVC : <UIAlertController: 0x7fdd1282a000>)**
Display alert : <UIAlertController: 0x7fdd1282a600>
**["Capture Alert : <UIAlertController: 0x7fdd1282a000>"]**
["TAP"]
Inside isFlag : true
Inside self: <ProfileVC: 0x7fdd13818600>
["Join: 3"]
["Join: 4"]
["Notify"]
XCTAssertEqual failed: ("Optional(0)") is not equal to ("Optional(1)")

观察:
)
两个测试的相同警报捕获。
加入计数增加到 4

函数中

tapButton()

我们正在调用警报操作处理程序,但并未解除实际警报。 如何解除 XCTestCase 中的警报? 注意:- showAlertOnWindow() 在 Utility class 中并且已被各种 classes 使用。

单元测试警报最安全的方法是让它们不实际出现。已记录但未显示警报:

  • 警报不会干扰测试
  • 测试不会互相干扰

为此,请尝试使用 https://github.com/jonreid/ViewControllerPresentationSpy。在测试代​​码中,实例化一个AlertVerifier。然后调用

  • verify()确认外观等属性
  • executeAction(forButton:) 调用特定按钮的闭包

无需关闭任何内容,因为 AlertVerifier 存在时不会显示真正的警报。