Swift 模拟类

标签 swift unit-testing mocking

据我所知,swift 中的模拟和 stub 方法没有可能的解决方案,就像我们在 objc 中使用 OCMock、Mockito 等一样。

我知道描述的技术 here .它在某些情况下非常有用,但现在我陷入了僵局:)

我有一个服务层,其中有类似契约(Contract)的东西(使用此参数调用此方法将返回该对象作为回调)。这是一个(大大简化的)示例:

class Bar
{
    func toData() -> NSData
    {
        return NSData()
    }
}

class Foo
{
    class func fromData(data: NSData) -> Foo
    {
        return Foo()
    }
}

class ServerManager
{
    let sharedInstance = ServerManager()

    class func send(request: NSData, response: (NSData)->())
    {
        //some networking code unrelated to the problem
        response(NSData())
    }
}

class MobileService1
{
    final class func Contract1(request: Bar, callback: (Foo) -> ())
    {
        ServerManager.send(request.toData()) { responseData in
            callback(Foo.fromData(responseData))
        }
    }
    //Contract2(...), Contract3(...), etc
}

因此在代码的某处我有以下场景:

func someWhereInTheCode(someBool: Bool, someObject: Bar)
{
    if someBool
    {
        MobileService1.Contract1(someObject) { resultFoo in
            //self.Foo = resultFoo
        }
    }
    else
    {
        //MobileService1.Contract2(...)
    }
}

现在的问题是我到底该如何测试它?在不触及合约本身的情况下,是否有更好的(用于测试)代码结构替代方案?

最佳答案

迟到总比不到好,我找到了解决方案。只需对 MobileService1(或更好的接口(interface))进行依赖注入(inject),然后轻松模拟它:

//declaring interface
protocol MobileServiceContracts: class {
    static func Contract1(request: Bar, callback: (Foo) -> ())
}

//make implementation to conform to interface
class MobileService1 : MobileServiceContracts
{
    final class func Contract1(request: Bar, callback: (Foo) -> ())
    {
        ServerManager.send(request.toData()) { responseData in
            callback(Foo.fromData(responseData))
        }
    }
    //Contract2(...), Contract3(...), etc
}

//inject service
func someWhereInTheCode(someBool: Bool, someObject: Bar, serviceProvider: MobileServiceContracts.Type = MobileService1.self)
{
    if someBool
    {
        serviceProvider.Contract1(someObject) { resultFoo in
            //self.Foo = resultFoo
        }
    }
    else
    {
        //MobileService1.Contract2(...)
    }
}

现在您可以轻松更改测试中的服务:

class MockedMobileService1: MobileServiceContracts
{
    static func Contract1(request: Bar, callback: (Foo) -> ()) {
        //do whatever with the mock
    }
}

someWhereInTheCode(false, someObject: Bar(), serviceProvider: MockedMobileService1.self)

最好的部分是使用默认值你仍然可以用旧的方式调用它(而不是阻止更改):

someWhereInTheCode(false, someObject: Bar())

关于Swift 模拟类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30761536/

相关文章:

swift - 如何使用 Swift 创建可折叠的 NSLog-Output?

windows - 是否有用于 UNIX 的 "Windows Batch file"模拟器/解释器?

java - 以当前时间为条件处理单元测试

c# - 如何模拟 Application.Current 进行单元测试?

java - 我如何模拟 Java 1.4 中的静态方法?

ios - 设置状态栏颜色 - 延迟?

ios - 在 View Controller 之间传递数据会产生 nil

ios - 为什么当我的基础数据源更新时 SwiftUI 会自动向后导航,我该如何避免这种行为?

javascript - 使用 Sinon 模拟 require() 函数

c# - 找不到名为 webdriver.json 的文件或 ID 为“WebDriver.FirefoxPreferences”的嵌入式资源