写单元测试RxSwift

时间:2016-06-22 10:55:09

标签: xcode swift unit-testing rx-swift

我得到了这门课,我想为其编写测试:

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()
    }
}

基本上我想测试授权Driver是否具有基于可能的CLAuthorizationStatuses的正确值。我需要一个正确方向的提示,因为我不熟悉RxSwift的单元测试。我想我最好的选择是创建一个CLLocationManager的模拟,它会在调用authorizationStatus()时返回一些CLAuthorizationStatus,之后我会检查授权Driver权限的值吗?

对于如何测试此LocationManager课程的任何解释表示赞赏。

2 个答案:

答案 0 :(得分:2)

好的,我搞定了。但是我不确定这是否是一个有效的解决方案。请随意在这里纠正我。

首先,我将我的LocationManager 类更改为:

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()
    }
}

基本上,现在可以提供符合我写的 LocationManagerProtocol 的新协议的类型。

接下来该协议实现了authorizationStatus功能,因此我可以模拟这个。

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

然后我创建了一个扩展到CLLocationManager 来实现这个协议:

import CoreLocation

extension CLLocationManager: LocationManagerProtocol {

}

从生产代码中调用: let locationManager = LocationManager(type: CLLocationManager.self)

从测试代码调用: let locationManager = LocationManager(type: AuthorizedLocationManager.self)let locationManager = LocationManager(type: ForbiddenLocationManager.self)

在我的测试类中,我添加了两个类覆盖authorizationStatus方法

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

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

我的测试用例:

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)
}

对不起,很长的帖子。我想我会告诉你关于我现在如何解决这个问题的所有信息。然而,正如我之前所说,我是新手,并希望听到关于这种方法的其他意见,以及这是否是一个有效的测试场景。

答案 1 :(得分:1)

看看RxSwift如何实现单元测试:https://github.com/ReactiveX/RxSwift/blob/4b3056b81f619c0bf12c6ee4e571582219e2b88d/Tests/RxSwiftTests/Tests/Observable%2BSingleTest.swift

至于使用CLLocationManager的模拟,是的,依赖注入将是最佳选择。