xcode - 为 RxSwift 编写单元测试

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

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

感谢任何有关如何测试此 LocationManager 类的解释。

最佳答案

好的,我让它工作了。但是我不确定这是否是一个有效的解决方案。请随时在这里纠正我。

首先,我将我的 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()
    }
}

现在基本上可以提供符合我编写的新协议(protocol)的类型 LocationManagerProtocol

接下来,协议(protocol)会实现 authorizationStatus 函数,这样我就可以模拟这个了。

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

然后我创建了一个CLLocationManager 的扩展 来实现这个协议(protocol):

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

抱歉发了这么长的帖子。我想我会告诉你我现在是如何解决这个问题的所有信息。然而,正如我之前所说,我对此并不陌生,很想听听有关这种方法的其他意见,以及这是否是一个有效的测试场景。

关于xcode - 为 RxSwift 编写单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37965953/

相关文章:

javascript - 在 WebStorm 中通过断言的测试失败

javascript - 用于单元测试的模拟 Angular bootstrap-ui 指令

iphone - 让阅读器应用程序全屏显示?

ios - SiriKit:自定义意图 - 本地化问题

ios - 如何通过快速按下按钮取消 localNotification?

ios - 使用 Swift 4 Decodable 将字符串 JSON 响应转换为 bool 值

unit-testing - 如何在VS 2017 .net core项目中生成单元测试用例?

ios - 在自定义子类 UICollectionViewCell 类中添加 subview

ios - Cocoa Touch Frameworks - 位码支持

swift - 如何使用 Xcode 7 使用 Swift 代码将 Button 放入 SpriteKit 中?