У меня была такая же проблема во время обновления от RxSwift2 до RxSwift3. В RxSwift2 я просто использовал протокол для rx-методов в моем производственном коде (например, rx_JSON()) и ввел модульный объект, придерживаясь этого протокола в моих модульных тестах.
После короткого объезда в дженерик, соответствующий тип и типа стирание я остановились со следующей установкой, которая точно соответствует моей RxSwift2-установку:
Например, чтобы скрыть URLSession в качестве конкретной реализации моих HTTP-вызовов, Я создал RemoteClient протокола:
protocol RemoteClient {
func json(url: URL) -> Observable<Any>
}
Затем в моем рабочем коде я ссылаться только этот протокол:
class SomeService {
var remoteClient: RemoteClient
init(remoteClient: RemoteClient) {
self.remoteClient = remoteClient
}
func getSomeData(_ id: String) -> Observable<JSONDictionary> {
let urlString = "..."
return remoteClient
.json(url: URL(string: urlString)!)
.map { data in
guard let show = data as? JSONDictionary else {
throw ParsingError.json
}
return show
}
}
}
в моей модульные тесты я создал фиктивные объекты, которые прилипают к протоколу RemoteClient:
class RemoteClientMock: RemoteClient {
var output: Any?
var verifyInput: ((URL) -> Void)?
func json(url: URL) -> Observable<Any> {
verifyInput?(url)
return Observable.create { observer in
if let data = self.output {
observer.on(.next(data))
}
observer.on(.completed)
return Disposables.create()
}
}
}
и впрыснуть их в тестируемой системе:
class SomeServiceTest: TestCase {
let disposeBag = DisposeBag()
func testGetSomeData() {
// setup
let expectation = self.expectation(description: "request succeeded")
let remoteClientMock = RemoteClientMock()
remoteClientMock.verifyInput = { url in
XCTAssertEqual(URL(string: "some url"), url)
}
remoteClientMock.output = ["id": 4711]
let service = SomeService(remoteClient: remoteClientMock)
// exercise
let observable = service.getSomeData("4711")
// verify
observable
.subscribe(onNext: { data in
XCTAssertEqual(4711, data["id"] as? Int)
expectation.fulfill()
})
.addDisposableTo(disposeBag)
waitForExpectations(timeout: 1, handler: nil)
}
}
Так что я просто скрыть RX-имена позади моего протокола. Я делаю это сейчас, используя адаптер:
let remoteClient = URLSessionRemoteClientAdapter(urlSession: URLSession.shared)
let service = SomeService(remoteClient: remoteClient)
struct URLSessionRemoteClientAdapter: RemoteClient {
let urlSession: URLSession
init(urlSession: URLSession) {
self.urlSession = urlSession
}
func json(url: URL) -> Observable<Any> {
return urlSession.rx.json(url: url)
}
}