2016-10-25 2 views
1

Im пытается добиться инъекции зависимостей с протоколами в Swift :)Дразнящий класс с расширениями Rx

Моя тестируемая система вызывает .rx.signIn() на зависимости, а это означает, что оно расширилось Реактивная где Base: ConcreteObject ,

Возможно ли установить протокол, готовый для приема вызовов .rx?

Любой маленький пример или альтернатива были бы весьма признательны.

ответ

1

У меня была такая же проблема во время обновления от 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) 
    } 
}