2017-03-03 54 views
0

我正在尝试使用测试驱动开发的情况。 我在Swift,Xcode,Apple,IOS,TDD甚至是我用于开发的macbook方面都没有经验。基本上我是一个非常陌生的.NET开发人员。测试驱动上传方法?

我目前的问题是由于我对如何进行测试无效方法的单元测试的无知而引起的。

我试图做一个方法,将图像发送到服务器。

但我在这里的问题是,我不知道如何测试不返回值的方法。

我想,我的方法将是与此类似:

public func Upload(_ image: UIImage) 

和我想象,我需要实现的URLSession,最终将不得不调用一个resume()方法的一些版本。但是我如何测试这个方法是否在不调用网络的情况下执行它应该做的事情?在那之后我怎么做一个集成测试,我可以看到预期的结果实际上是一个文件上传到服务器? 目前服务器将在我正在开发的计算机上,但实际的软件将从我已发布的testIphone运行。

我一直在网上搜索了几天,我遇到过的最好的一直是这个链接http://swiftdeveloperblog.com/image-upload-with-progress-bar-example-in-swift/ 但它只接近零碎,我认为将成为解决方案的一部分而不是测试解决方案的测试。

我认为这很重要,我非常反对为测试目的创建复杂性。测试应该简单而直接。

回答

2

要采取的方法是使用一个测试双重检查您的upload方法进行正确的网络调用。您将对网络库进行异步调用,该网络库可能为URLSession或可能是另一个库,如AlamoFire。它应该无关紧要你的upload方法哪个库正在使用。

为了达到这个目的,你想避免直接使用URLSession,并使用一个符合接口的包装器,然后你可以在你的测试中进行模拟。这意味着您的代码将在运行时使用不同的网络类实现,而不是在测试时间,并且您将根据需要“注入”正确的代码。

例如,你可以有这个接口来您的网络库:

protocol NetworkRequesting { 
    func post(data: Data, url: URL) 
} 

用下面的真正落实到在运行时使用:

struct NetworkRequester: NetworkRequesting { 
    func post(data: Data, url: URL) { 
     let session = URLSession() 
     let task = session.uploadTask(with: URLRequest(url: url), from: data) 
     task.resume() 
    } 
} 

然而,在测试时,您可以使用下面的模拟,而不是:

class MockNetworkRequester: NetworkRequesting { 
    var didCallPost = false 
    var spyPostData: Data? = nil 
    var spyPostUrl: URL? = nil 
    func post(data: Data, url: URL) { 
     didCallPost = true 
     spyPostData = data 
     spyPostUrl = url 
    } 
} 

,然后给下面的类测试:

class ImageUploader { 
    let networkRequester: NetworkRequesting 
    init(networkRequester: NetworkRequesting) { 
     self.networkRequester = networkRequester 
    } 

    func upload(image: UIImage, url: URL) { 

    } 
} 

可以测试的upload实施像这样:

class UploadImageTests: XCTestCase { 
    func test_uploadCallsPost() { 
     let mockNetworkRequester = MockNetworkRequester() 
     let uploader = ImageUploader(networkRequester: mockNetworkRequester) 
     uploader.upload(image: UIImage(), url: URL(string:"http://example.com")!) 
     XCTAssert(mockNetworkRequester.didCallPost) 
    } 
} 

目前,由于upload做什么,测试将失败,但是如果你把被测以下进级,测试将通过:

func upload(image: UIImage, url: URL) { 
    guard let otherUrl = URL(string:"https://example.org") else { return } 
    networkRequester.post(data: Data(), url: otherUrl) 
} 

这就是你的第一个TDD周期。很明显,它还没有像你期望的那样表现出来,所以你需要编写另一个测试来确保使用的URL是你期望的,或者传递的数据是你期望的。

有很多方法可以让您的代码在运行时使用真正的网络请求者,您可以让init方法使用默认参数值使其使用NetworkRequester或使用静态工厂方法创建它,还有其他选项,如控制反转,这远远超出了这个答案的范围。

要记住的重要事情是,你要测试你做出正确的电话到网络框架,你没有测试的网络框架。我喜欢保持我的协议接口非常具有声明性,在任何框架中传递请求所需的东西,但是您可能会发现您更喜欢更接近金属,并基本反映URLSession的实现 - 这取决于您,还有更多在我看来,这是一门艺术而不是科学。

+0

我喜欢你的方法,我现在要试试:)谢谢你花时间给我这个答案 – Helbo