夫特 - 使用XCTest测试含有闭合功能

问题描述 投票:6回答:2

我是相当新的雨燕和我目前正在写(使用XCTest)测试以下功能的单元测试:

func login(email: String, password: String)  {

    Auth.auth().signIn(withEmail: email, password: password) { (user, error) in
        if let _error = error {
            print(_error.localizedDescription)
        } else {
            self.performSegue(identifier: "loginSeg")
        }
    }
}

我的研究已确定,我需要为XCTest默认情况下这意味着它不会等待关闭完成运行同步执行使用XCTestExpectation功能(请纠正我,如果我错了)。

请告诉我扔我了我是如何测试的登录功能,因为它本身调用异步函数Auth.auth().signIn()。我想测试签到是否成功。

道歉,如果这已经回答了,但我找不到直接解决这个问题的答案。

谢谢

更新:

从回答一些帮助,并进一步研究,我通过登录功能修改为使用转义关闭:

func login(email: String, password: String, completion: @escaping(Bool)->())  {

    Auth.auth().signIn(withEmail: email, password: password) { (user, error) in
        if let _error = error {
            print(_error.localizedDescription)
            completion(false)
        } else {
            self.performSegue(identifier: "loginSeg")
            completion(true)
        }
    }
}

然后,我通过以下方式进行测试:

func testLoginSuccess() {

    // other setup

    let exp = expectation(description: "Check Login is successful")

    let result = login.login(email: email, password: password) { (loginRes) in
        loginResult = loginRes
        exp.fulfill()
    }

    waitForExpectations(timeout: 10) { error in
        if let error = error {
            XCTFail("waitForExpectationsWithTimeout errored: \(error)")
        }
        XCTAssertEqual(loginResult, true)
    }
}

我的测试功能现在已经成功地测试登录功能。

希望这可以帮助别人,因为它给我留下难住了一段时间:)

ios swift xcode testing xctest
2个回答
5
投票

以验证该呼叫是一个建筑的边界。单元测试是更快,更可靠,如果他们去到这样的边界,但不越过他们。我们可以通过分离协议背后的验证单身做到这一点。

我猜在signIn的签名。不管是什么,复制并粘贴到一个协议:

protocol AuthProtocol {
    func signIn(withEmail email: String, password: String, completion: @escaping (String, NSError?) -> Void)
}

这可以作为完整的验证界面的薄片,只取你想要的部分。这是接口分离原则的一个例子。

然后延伸验证,以符合该协议。它已经这样做,所以一致性是空的。

extension Auth: AuthProtocol {}

现在,在您的视图控制器,解压直接调用Auth.auth()与默认值的属性:

var auth: AuthProtocol = Auth.auth()

谈谈这个属性,而不是直接Auth.auth()

auth.signIn(withEmail: email, …etc…

这引入了一个缝。测试可以用一个试谍照,记录auth是如何被调用的实现替换signIn

final class SpyAuth: AuthProtocol {
    private(set) var signInCallCount = 0
    private(set) var signInArgsEmail: [String] = []
    private(set) var signInArgsPassword: [String] = []
    private(set) var signInArgsCompletion: [(String, Foundation.NSError?) -> Void] = []

    func signIn(withEmail email: String, password: String, completion: @escaping (String, Foundation.NSError?) -> Void) {
        signInCallCount += 1
        signInArgsEmail.append(email)
        signInArgsPassword.append(password)
        signInArgsCompletion.append(completion)
    }
}

测试可以注入SpyAuth到视图控制器,拦截一切通常会去验证。正如你所看到的,这包括完成关闭。我会写

  • 一个测试,以确认通话次数和不缝合参数
  • 另一项测试,以获得拍摄的关闭和成功调用它。
  • 我还失败调用它,如果你的代码没有一个print(_)声明。

最后,还有塞格斯的问题。苹果并没有给我们任何方式进行单元测试。作为一种变通方法,可以使一个部分模拟。事情是这样的:

final class TestableLoginViewController: LoginViewController {
    private(set) var performSegueCallCount = 0
    private(set) var performSegueArgsIdentifier: [String] = []
    private(set) var performSegueArgsSender: [Any?] = []

    override func performSegue(withIdentifier identifier: String, sender: Any?) {
        performSegueCallCount += 1
        performSegueArgsIdentifier.append(identifier)
        performSegueArgsSender.append(sender)
    }
}

有了这个,你可以拦截调用performSegue。这不是理想的,因为它是一个遗留代码技术。但它应该让你开始。

final class LoginViewControllerTests: XCTestCase {
    private var sut: TestableLoginViewController!
    private var spyAuth: SpyAuth!

    override func setUp() {
        super.setUp()
        sut = TestableLoginViewController()
        spyAuth = SpyAuth()
        sut.auth = spyAuth
    }

    override func tearDown() {
        sut = nil
        spyAuth = nil
        super.tearDown()
    }

    func test_login_shouldCallAuthSignIn() {
        sut.login(email: "EMAIL", password: "PASSWORD")

        XCTAssertEqual(spyAuth.signInCallCount, 1, "call count")
        XCTAssertEqual(spyAuth.signInArgsEmail.first, "EMAIL", "email")
        XCTAssertEqual(spyAuth.signInArgsPassword.first, "PASSWORD", "password")
    }

    func test_login_withSuccess_shouldPerformSegue() {
        sut.login(email: "EMAIL", password: "PASSWORD")
        let completion = spyAuth.signInArgsCompletion.first

        completion?("DUMMY", nil)

        XCTAssertEqual(sut.performSegueCallCount, 1, "call count")
        XCTAssertEqual(sut.performSegueArgsIdentifier.first, "loginSeg", "identifier")
        let sender = sut.performSegueArgsSender.first
        XCTAssertTrue(sender as? TestableLoginViewController === sut,
            "Expected sender \(sut!), but was \(String(describing: sender))")
    }
}

绝对没有异步这里,所以没有waitForExpectations。我们捕捉的关闭,我们称之为封闭。

(这样的东西将在iOS unit testing book I'm currently writing深度覆盖。)


0
投票

乔恩的答案是优秀的,我不能添加评论,所以我会在这里我的建议。对于那些谁拥有(因为任何原因)的静态/类函数,而不是一个单独或实例功能,这可以帮助你:

举例来说,如果你有Auth.signIn(withEmail: emai...其中signIn是静态函数。相反,使用:

var auth: AuthProtocol = Auth.auth()

采用:

var auth: AuthProtocol.Type = Auth.self

并指派它像这样

sut.auth = SpyAuth.self
© www.soinside.com 2019 - 2024. All rights reserved.