ios - Swift 中可测试的异步设计模式

标签 ios swift unit-testing asynchronous

我正在学习 Swift 中的测试驱动开发。当我意识到我经常用于异步请求的委托(delegate)模式很难测试时,我遇到了困难。我了解到,如果某些东西难以测试,那么实现背后的设计模式可能会更好。这让我感到困惑,因为我认为我使用的委托(delegate)模式很常见,我想知道其他人是如何处理这个问题的。

模式:

我编写了一个服务,它在采用委托(delegate)实例的静态函数中执行异步请求。委托(delegate)实例符合需要实现成功和失败方法的协议(protocol)。我设计了一个点击 Google.com 的示例。请忽略此示例中的类型安全问题。我正在运行的用于访问端点并解析 JSON 的实际代码更安全。我只是想提出一小段代码来描述导致测试困难的问题:

protocol GoogleServiceDelegate {
    func gotGoogle(str: String);
    func gotError(str: String);
}

struct GoogleService {
    static func getGoogle(delegate: GoogleServiceDelegate) {
        let url: NSURL! = NSURL(string: "http://google.com")
        NSURLSession.sharedSession().dataTaskWithURL(url) { data, response, error in
            if let data = data {
                let str: NSString! = NSString(data: data, encoding: NSUTF8StringEncoding)
                delegate.gotGoogle(str as String)
            } else {
                delegate.gotError("\(error)")
            }
        }
    }
}

下面是说明问题的测试:

class AsyncTestingTests: XCTestCase {

    func testExample() {
        let responseExpectation = expectationWithDescription("Got google response!")

        struct GoogleDelegate: GoogleServiceDelegate {
            func gotGoogle(str: String) {
                // expectations about response
                responseExpectation.fulfill()
            }

            func gotError(str: String) {
                // expectations about error
                responseExpectation.fulfill()
            }
        }

        let myGoogleServiceDelegate = GoogleDelegate()
        GoogleService.getGoogle(myGoogleServiceDelegate)

        waitForExpectationsWithTimeout(5) { _ in
            print("Never got a response from Google :(")
        }
    }
}

问题出现在两个 .fulfill() 行。我从 Xcode 收到以下错误:

Struct declaration cannot close over value 'responseExpectation' defined in outer scope

我理解错误,但不确定要调整什么...是否有我可以在测试中使用的解决方法,或者是否有比我正在尝试的更好(易于测试)的异步回调模式?如果您知道更好的可测试解决方案,您介意花时间写下示例吗?

最佳答案

是的,您不能关闭在结构外部定义的变量,要解决这个问题,我们需要使用闭包/函数并将其传递给结构。 struct 中的方法可以在收到响应时调用它。

    func testExample() {
      let responseExpectation = expectationWithDescription("Got google response!")

//Let a function capture the fulfilling of the expectation
      func fullFillExpectation(){

        responseExpectation.fullFill()
      }

      struct GoogleDelegate: GoogleServiceDelegate {

        var fullFiller : (()->Void)!
        func gotGoogle(str: String) {
          // expectations about response via invoke the closure
          fullFiller()
        }

        func gotError(str: String) {
          // expectations about error - invoke the closure
          fullFiller()
        }
      }

      //Create the delegate with full filler function.
      let myGoogleServiceDelegate = GoogleDelegate(fullFiller: fullFillExpectation)
      GoogleService.getGoogle(myGoogleServiceDelegate)

      waitForExpectationsWithTimeout(5) { _ in
        print("Never got a response from Google :(")
      }
    }
    }

PS:我无法测试这个,请测试并告诉我。

关于ios - Swift 中可测试的异步设计模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34781878/

相关文章:

ios - 在不延迟 UIViewController segue 的情况下运行 CPU 密集型代码?如何在 UIViewController 出现后*仅执行一次代码?

iphone - 使用什么样的标签

ios - Swift 准备被 TabBarController 打断的 segue

Swift - 加载 PNG 而不缓存

java - 以编程方式实例化具有 Spring 生命周期注释的类的对象

ios - 我想设计一个屏幕,根据特定条件在同一屏幕上显示不同数量的视频

ios - 打开 iOS 应用程序后,未调用 "continue userActivity:"方法 - Firebase 动态链接

ios - FBSDK 和 pod : Command PhaseScriptExecution failed with a nonzero exit code

unit-testing - 如何在 Resharper 7 中支持 MBUnit 测试

c# - 如何在 .NET 中测试可执行文件