2016-06-22 10 views
1

bekam ich diese Klasse, die Ich mag würde für Tests schreiben:schreiben Unit-Tests für RxSwift

import CoreLocation 
import RxCocoa 
import RxSwift 

struct LocationManager { 

    private (set) var authorized: Driver<Bool> 
    private let coreLocationManager = CLLocationManager() 

    init() { 
     coreLocationManager.distanceFilter = kCLDistanceFilterNone 
     coreLocationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation 

     authorized = Observable.deferred { [weak coreLocationManager] in 
      let status = CLLocationManager.authorizationStatus() 
      guard let coreLocManager = coreLocationManager else { 
       return Observable.just(status) 
      } 
      return coreLocManager 
       .rx_didChangeAuthorizationStatus 
       .startWith(status) 
      } 
      .asDriver(onErrorJustReturn: CLAuthorizationStatus.NotDetermined) 
      .map { 
       switch $0 { 
       case .AuthorizedWhenInUse: 
        return true 
       default: 
        return false 
       } 
     } 

     coreLocationManager.requestWhenInUseAuthorization() 
    } 
} 

Grundsätzlich möchte ich testen, ob das genehmigte Driver die basierend auf möglichen CLAuthorizationStatuses richtigen Wert hat. Ich brauche einen Hinweis in die richtige Richtung, da ich mit Unit-Tests mit RxSwift nicht vertraut bin. Ich denke, meine beste Option ist es, eine Kopie von CLLocationManager zu erstellen, die einige CLAuthorizationStatus zurückgibt, wenn authorizationStatus() aufgerufen wird und ich würde dann den Wert der autorisierten Driver richtig überprüfen?

Jede Erklärung, wie man diese LocationManager Klasse testet, wird geschätzt.

Antwort

2

OK Ich habe es funktioniert. Ich bin mir jedoch nicht sicher, ob dies eine gültige Lösung ist. Fühlen Sie sich frei, mich hier zu korrigieren.

Zunächst einmal änderte ich meine Location Klasse dazu:

import CoreLocation 
import RxCocoa 
import RxSwift 

struct LocationManager<T where T: LocationManagerProtocol> { 

    private (set) var authorized: Driver<Bool> 
    private let coreLocationManager = CLLocationManager() 

    init(type: T.Type) { 
     coreLocationManager.distanceFilter = kCLDistanceFilterNone 
     coreLocationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation 

     authorized = Observable.deferred { [weak coreLocationManager] in 
      let status = type.authorizationStatus() 
      guard let coreLocManager = coreLocationManager else { 
       return Observable.just(status) 
      } 
      return coreLocManager 
       .rx_didChangeAuthorizationStatus 
       .startWith(status) 
      } 
      .asDriver(onErrorJustReturn: CLAuthorizationStatus.NotDetermined) 
      .map { 
       switch $0 { 
       case .AuthorizedWhenInUse: 
        return true 
       default: 
        return false 
       } 
     } 

     coreLocationManager.requestWhenInUseAuthorization() 
    } 
} 

Grundsätzlich ist es nun möglich, einen Typ zu einem neuen Protokoll entspricht I bereitzustellen LocationManagerProtocol schrieb.

Als nächstes implementiert das Protokoll die authorizationStatus Funktion, so dass ich dies verspotten kann.

protocol LocationManagerProtocol { 
    static func authorizationStatus() -> CLAuthorizationStatus 
} 

Dann habe ich eine Erweiterung CLLocationManager dieses Protokoll zu implementieren:

import CoreLocation 

extension CLLocationManager: LocationManagerProtocol { 

} 

Anruf von Produktionscode:let locationManager = LocationManager(type: CLLocationManager.self)

Aufruf von Testcode:let locationManager = LocationManager(type: AuthorizedLocationManager.self) oderI 10

In meinem Test-Klasse hinzugefügt, diese beiden Klassen zwei Überschreibung der authorizationStatus Methode:

class AuthorizedLocationManager: LocationManagerProtocol { 
    static func authorizationStatus() -> CLAuthorizationStatus { 
     return .AuthorizedWhenInUse 
    } 
} 

class ForbiddenLocationManager: LocationManagerProtocol { 
    static func authorizationStatus() -> CLAuthorizationStatus { 
     return .Denied 
    } 
} 

Meine Testfälle:

func testLocationAuthorizationPermitted() { 
    let locationManager = LocationManager(type: AuthorizedLocationManager.self) 

    locationManager.authorized 
    .driveNext { authorized in 
     XCTAssertTrue(authorized) 
    } 
    .addDisposableTo(disposeBag) 
} 

func testLocationAuthorizationRejected() { 
    let locationManager = LocationManager(type: ForbiddenLocationManager.self) 

    locationManager.authorized 
     .driveNext { authorized in 
      XCTAssertFalse(authorized) 
     } 
     .addDisposableTo(disposeBag) 
} 

Sorry für den langen Post. Ich dachte, ich würde dir alle Informationen geben, wie ich das jetzt gelöst habe. Wie ich bereits sagte, bin ich neu und würde gerne andere Meinungen zu diesem Ansatz hören und ob dies ein gültiges Testszenario ist oder nicht.