ios – Swift – 使用XCTest来测试包含闭包的函数
我对
Swift相当新,我正在尝试编写一个单元测试(使用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") } } } 我的研究已经确定我需要使用XCTestExpectation功能,因为默认情况下XCTest同步执行意味着它不会等待闭包完成运行(如果我错了请纠正我). 什么让我失望的是我如何测试登录功能,因为它本身调用异步函数Auth.auth().signIn().我正在尝试测试signIn是否成功. 如果这已经得到回答,我很抱歉,但我找不到直接解决这个问题的答案. 谢谢 更新: 在答案和进一步研究的帮助下,我通过登录功能进行了修改,以使用转义闭包: func login(email: String,password: String,completion: @escaping(Bool)->()) { Auth.auth().signIn(withEmail: email,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) } } 我的测试功能现在成功测试登录功能. 希望这有助于某人,因为它让我感到难过一段时间:) 解决方法
对Auth的调用是一个架构边界.如果单元测试达到这样的边界,但是不要越过它们,则单元测试更快,更可靠.我们可以通过隔离协议背后的Auth单例来实现这一点.
我猜着signIn的签名.无论是什么,将其复制并粘贴到协议中: protocol AuthProtocol { func signIn(withEmail email: String,completion: @escaping (String,NSError?) -> Void) } 这可以作为完整Auth界面的一小部分,只占用您想要的部分.这是接口隔离原理的一个例子. 然后扩展Auth以符合此协议.它已经存在,所以一致性是空的. extension Auth: AuthProtocol {} 现在在视图控制器中,将对Auth.auth()的直接调用解压缩到具有默认值的属性中: var auth: AuthProtocol = Auth.auth() 请与此属性对话,而不是直接与Auth.auth()对话: auth.signIn(withEmail: email,…etc… 这介绍了一个Seam.测试可以使用作为测试间谍的实现替换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,Foundation.NSError?) -> Void) { signInCallCount += 1 signInArgsEmail.append(email) signInArgsPassword.append(password) signInArgsCompletion.append(completion) } } 测试可以将SpyAuth注入到视图控制器中,拦截通常用于Auth的所有内容.如您所见,这包括完成关闭.我会写的 >一个测试来确认调用计数和非闭包参数 最后,还有segues的问题. Apple没有给我们任何单元测试方法.作为一种解决方法,您可以进行部分模拟.像这样的东西: 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,"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中深入讨论.) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |