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

时间:2016-01-14 04:43:24

标签: ios swift unit-testing asynchronous

我在Swift学习测试驱动开发。当我意识到我经常用于异步请求的委托模式很难测试时,我碰壁了。我已经了解到,如果某些事情难以测试,实施背后的设计模式可能会更好。这让我感到困惑,因为我认为我使用的代表模式很常见,我想知道其他人如何处理这个问题。

模式:

我编写了一个服务,它在一个带有委托实例的静态函数中执行异步请求。委托实例符合需要实现成功和失败方法的协议。我设计了一个打入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收到以下错误:

  

结构声明不能超过价值' responseExpectation'在外部范围内定义

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

1 个答案:

答案 0 :(得分:1)

是的,你不能关闭在struct之外定义的变量,为了解决方法,我们需要使用闭包/函数并将它传递给struct。 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:我无法测试,请测试并告诉我。