如何为我的 ViewModel 编写好的测试? (RxSwift)

How to writ good test for my ViewModel? (RxSwift)

我为我的 ViewModel 编写了一些测试。我在这个项目中使用 RxSwift。我以前从未写过单元测试,所以我想问你关于它们的正确性。下次我可以做些什么更好?当我使用 RxSwift 编写测试时,这对我来说并不困难。所有测试都通过了,但我不知道它们是否是“好的测试”。感谢您的帮助。

视图模型:

class SettingsViewModel {
private let storage = Storage.shared
private let disposeBag = DisposeBag()
let userSettings = BehaviorRelay<UserSettings>(value: UserSettings(name: "", tags: []))

init() {
    subscribe()
}

private func subscribe() {
    storage.currentUserSettings()
        .subscribe(onNext: { settings in
            if let settings = settings {
                self.userSettings.accept(settings)
            }
        })
        .disposed(by: disposeBag)
}

func saveName(_ name: String) {
    saveSettings(name: name, tags: userSettings.value.tags)
}

func addTag(_ tag: String) {
    let newTags = userSettings.value.tags + [tag]
    saveSettings(name: userSettings.value.name, tags: newTags)
}

func removeTag(_ index: Int) {
    var newTags = userSettings.value.tags
    newTags.remove(at: index)
    saveSettings(name: userSettings.value.name, tags: newTags)
}

private func saveSettings(name: String, tags: [String]) {
    let newSettings = UserSettings(name: name, tags: tags)
    Storage.shared.saveUserSettings(newSettings)
}

}

测试class:

class SettingsViewModelTests: XCTestCase {

func test_userSettingsSaving_includesAddingName() {
    let sut = SettingsViewModel()
    let userSettings = UserSettingsSpy(sut.userSettings)
    
    sut.saveName("George")
    XCTAssertEqual(userSettings.settings.name, "George")
    
    sut.saveName("Mike")
    XCTAssertEqual(userSettings.settings.name, "Mike")
}

func test_userSettingsSaving_includesAddingTag() {
    let sut = SettingsViewModel()
    let userSettings = UserSettingsSpy(sut.userSettings)
    
    sut.addTag("Book")
    
    var savedTags: [String] = []
    Storage.shared.currentUserSettings()
        .subscribe(onNext: { settings in
            if let tags = settings?.tags {
                savedTags = tags
            }
        })
        .dispose()
    
    XCTAssertEqual(userSettings.settings.tags, savedTags)
}

func test_userSettingsSaving_includesRemovingTag() {
    let sut = SettingsViewModel()
    let userSettings = UserSettingsSpy(sut.userSettings)
    
    sut.addTag("TestTagToRemove")
    sut.removeTag(0)
    
    var savedTags: [String] = []
    Storage.shared.currentUserSettings()
        .subscribe(onNext: { settings in
            if let tags = settings?.tags {
                savedTags = tags
            }
        })
        .dispose()
    
    XCTAssertEqual(userSettings.settings.tags, savedTags)
}

class UserSettingsSpy {
    private let disposeBag = DisposeBag()
    private(set) var settings = UserSettings(name: "", tags: [])
    
    init(_ observable: BehaviorRelay<UserSettings>) {
        observable
            .subscribe(onNext: { settings in
                self.settings = settings
            })
            .disposed(by: disposeBag)
    }
}

}

检查测试正确性的一种简单方法是更改​​被测系统并查看您的测试是否标记了错误。如果他们不这样做,那么这就是您测试中的一个漏洞。例如,以下视图模型将通过您的测试:

struct Storage {
    static let shared = Storage()
    func currentUserSettings() -> Observable<UserSettings?> { .just(nil) }
}

struct SettingsViewModel {
    let userSettings = BehaviorRelay<UserSettings>(value: UserSettings())
    
    func saveName(_ value: String) {
        userSettings.accept(UserSettings(name: value, tags: []))
    }
    
    func addTag(_ value: String) { }
    
    func removeTag(_ value: Int) { }
}

struct UserSettings {
    var name: String = ""
    var tags: [String] = []
}

上面的代码显然缺少一些重要的功能,这意味着您的测试不完整。