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

Time:02-11

I wrote some tests for my ViewModel. I use RxSwift in this project. I have never before write unit tests, so i want to ask you about correctness of them. What can I do better next time? It is little difficult for me when I write tests while I use RxSwift. All tests passed, but I don't know if they are "good tests". Thanks for your help.

ViewModel:

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

}

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

}

CodePudding user response:

An easy way to check the correctness of your tests is to change the system under test and see if your tests flag the error. If they don't, then that is a hole in your tests. For example, the following view model will pass your tests:

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] = []
}

The code above is obviously missing some important functionality which means your tests are incomplete.

  • Related