【问题标题】:Swift Mocking ClassSwift 模拟类
【发布时间】:2015-06-10 16:01:16
【问题描述】:

据我所知,没有像我们在 objc 中使用 OCMock、Mockito 等那样快速模拟和存根方法的解决方案。

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

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

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(...)
    }
}

现在的问题是我怎么能测试这个?是否有更好的(用于测试)代码结构替代方案而不涉及合约本身?

【问题讨论】:

    标签: swift unit-testing mocking


    【解决方案1】:

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

    //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())
    

    【讨论】:

    • 因此您必须对产品代码进行大量更改。相当冒险
    【解决方案2】:

    同时,您可以使用Cuckoo 进行模拟,类似于Mockito。

    示例类:

    class ExampleObject {
    
        var number: Int = 0
    
        func evaluate(number: Int) -> Bool {
            return self.number == number
        }
    
    }
    
    class ExampleChecker {
    
        func check(object: ExampleObject) -> Bool {
            return object.evaluate(5)
        }
    
    }
    

    示例测试:

    @testable import App
    import Cuckoo
    import XCTest
    
    class ExampleCheckerTests: XCTestCase {
    
        func testCheck() {
            // 1. Arrange
            let object = MockExampleObject().spy(on: ExampleObject())
            stub(object) { object in
                when(object.evaluate(any())).thenDoNothing()
            }
            let checker = ExampleChecker()
    
            // 2. Action
            checker.check(object)
    
            // 3. Assert
            _ = verify(object).number.get
            verify(object).evaluate(any())
            verifyNoMoreInteractions(object)
        }
    
    }
    

    【讨论】:

      【解决方案3】:

      有一个更好的框架叫做Mockingbird

      设置非常简单,并且在构建应用程序以运行测试时动态构建您的模拟。 Here’s an article 解释了它的一些工作原理

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-08-02
        • 1970-01-01
        • 2016-10-22
        • 1970-01-01
        • 2019-08-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多