我正在尝试对简单的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>;
}
}
给我一个问题是我在嘲笑的方法内部有通用参数T,该参数是将请求解码为的类。我不知道这个参数,直到我调用此函数为止,基本上是在MockHttpClient类中为存储为我创建的makeRequest存根数据的属性创建的:
stubbedMakeRequestResult: Observable<Any>
,返回后,我试图将其强制转换为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)
}
}
答案 0 :(得分:0)
由于Swift中的泛型are invariant,除非Observable<Any>
为Observable<T>
,否则T
永远不会转换为Any
。这就是导致崩溃的原因,因为当您将值分配给stubbedMakeRequestResult
时,具体的Observable
会转换为Observable<Any>
,并且这里没有转折点。
为避免这种情况,您可以做的是将stubbedMakeRequestResult
设为Any
,因为这不会在幕后进行任何转换。一个小问题是您松开了类型安全性和推断,但是可以通过以下功能修复此存根:
class MockHttpClient: GenericHttpClientInterface {
...
var stubbedMakeRequestResult: Any!
func stubMakeRequestResult<T>(_ result: Observable<T>) {
stubbedMakeRequestResult = result
}
...
}