【问题标题】:Mocking a class with generic parameter function inside Swift在 Swift 中模拟具有泛型参数函数的类
【发布时间】:2020-08-28 11:52:15
【问题描述】:

我正在尝试对简单的 HttpClient 行为进行单元测试。为此,我创建了一个 GenericHttpClientInterface 协议和实现该协议的具体类 GenericHttpClient。

protocol GenericHttpClientInterface {
    func makeRequest<T: Decodable>(request: URLRequest) -> Observable<T>
}

class GenericHttpClient: GenericHttpClientInterface {
    func makeRequest<T: Decodable>(request: URLRequest) -> Observable<T> {
        return URLSession.shared.rx.data(request: request).jsonDecode(to: T.self)
    }
}

我想要实现的是模拟该类:

class MockHttpClient: GenericHttpClientInterface {
    var invokedMakeRequestCount = 0
    var invokedMakeRequestParameters: (request: URLRequest, Void)?
    var stubbedMakeRequestResult: Observable<Any>!
    func makeRequest<T: Decodable>(request: URLRequest) -> Observable<T> {
        invokedMakeRequestCount += 1
        invokedMakeRequestParameters = (request, ())
        return stubbedMakeRequestResult as! Observable<T>;
    }
}

给 ma 带来问题的是,我正在模拟的方法具有通用参数 T,其中是一个请求将被解码到的类。我不知道这个参数,直到我调用这个函数,所以基本上在 MockHttpClient 类中为一个存储我创建的 makeRequest 存根数据的属性:

stubbedMakeRequestResult: Observable&lt;Any&gt;

返回它后,我试图将其转换为结果类型 Observable。这给了我一个警告

Cast from 'Observable<Any>?' to unrelated type 'Observable<T>' always fails

结果

Thread 1: signal SIGABRT.

知道如何存根这些数据吗?

创建 SIGABRT 的示例测试:

class GenericHttpTest: XCTestCase {
    
    var sut: Repository!
    var mockHttpClient: MockHttpClient!
    
    override func setUp() {
        mockHttpClient = MockHttpClient()
        sut = Repository(httpClient: mockHttpClient)
    }
        
    let test_mocked_data_stub = DataModelStruct(args: DataModelStruct.InsideModelStruct(foo1: "bar"))

    func test_should_return_mocked_data_from_mock_http_client() {
        mockHttpClient.stubbedMakeRequestResult = Observable.just(test_mocked_data_stub)
        
        let response = try! sut.getFooBar().toBlocking().first()

        XCTAssertEqual(response, test_mocked_data_stub)
    }
}

【问题讨论】:

  • 您似乎理解了这个错误 - 您的存根类型与 makeRequest 的返回类型不同。因此,您要么需要在makeRequest 中创建存根数据并检查类型。或者你可能会为你正在使用的类型重载makeRequest

标签: ios swift unit-testing mocking xctest


【解决方案1】:

由于 Swift 中的泛型 are invariantObservable&lt;Any&gt; 永远不会转换为 Observable&lt;T&gt;,除非 TAny。这就是导致崩溃的原因,因为当您将值分配给stubbedMakeRequestResult 时,具体的Observable 将转换为Observable&lt;Any&gt;,并且从这里没有转折点。

要避免这种情况,您可以做的是将stubbedMakeRequestResult 设为Any,因为这不会在幕后进行任何转换。一个小问题是你失去了类型安全和推理,但你可以通过一个函数来修复这个存根:

class MockHttpClient: GenericHttpClientInterface {
    ...
    var stubbedMakeRequestResult: Any!
    
    func stubMakeRequestResult<T>(_ result: Observable<T>) {
        stubbedMakeRequestResult = result
    }
    ...
}

【讨论】:

    猜你喜欢
    • 2012-04-12
    • 1970-01-01
    • 2010-10-04
    • 1970-01-01
    • 1970-01-01
    • 2022-01-04
    • 2018-02-18
    • 1970-01-01
    相关资源
    最近更新 更多