So I'm completely new to testing and I just needed some help figuring out for example how I would write a test for each of the three cases in the enum of the View Model (none, dontSeeProvider, showAllProviders).

enum ProvidersButtonType {
case none, dontSeeProvider, showAllProviders
}

I haven't been able to figure out how to write a test for cases "showAllProviders" and "dontSeeProviders".

This is the View Model:

import RxSwift
import RxCocoa

struct TopProvidersPickerItem {
let provider: MVPD

let logoImage: Observable<UIImage>

init(provider: MVPD, imageLoader: DecodableProviding) {
    self.init(provider: provider, logoImage: imageLoader.image(fromURL: provider.logoUrl))
}

init(provider: MVPD, logoImage: Observable<UIImage>) {
    self.provider = provider
    self.logoImage = logoImage.catchErrorJustReturn(UIImage())
}
}

enum ProvidersButtonType {
case none, dontSeeProvider, showAllProviders
}

struct TopProvidersPickerViewModel {
var caption: String {
    return "Get access to more full episodes by signing in with your TV Provider"
}

let buttonType = Variable<ProvidersButtonType>(.none)
let items: Observable<[TopProvidersPickerItem]>
let selectedItem: PublishSubject<TopProvidersPickerItem> = PublishSubject()
let showAllProvidersTrigger: PublishSubject<Void> = PublishSubject()
let mvpdPicked: Observable<MVPD>

init(topProviders: Observable<[MVPD]>, imageLoader: DecodableProviding) {
    let items = topProviders.map({ mvpds in
        return mvpds.map { mvpd in
            TopProvidersPickerItem(provider: mvpd, imageLoader: imageLoader)
        }
    })
    self.init(items: items)
}

init(items: Observable<[TopProvidersPickerItem]>) {
    self.items = items
    mvpdPicked = selectedItem.map { $0.provider }
    let buttonType = items.map { (array) -> ProvidersButtonType in
        if array.count > 12 {
            return .showAllProviders
        } else {
            return .dontSeeProvider
        }
    }
    buttonType.bind(to: self.buttonType)
}

}

This is the View Controller:

import UIKit
import RxCocoa
import RxSwift

public class ProviderCollectionViewCell: UICollectionViewCell {
    @IBOutlet public private(set) weak var imageView: UIImageView!
}

public class TopProvidersPickerViewController: UIViewController, 
ViewModelHolder {
var viewModel: TopProvidersPickerViewModel! = nil
private let bag = DisposeBag()

@IBOutlet public private(set) weak var collectionView: UICollectionView!
@IBOutlet public private(set) weak var captionLabel: UILabel!
@IBOutlet weak var viewAllProvidersButton: UIButton!

override public func viewDidLoad() {
    super.viewDidLoad()
    captionLabel.text = viewModel.caption
    setupRx()
}

private func setupRx() {
    viewModel.buttonType.asObservable().subscribe(onNext: { [button = self.viewAllProvidersButton] type in
        button?.isHidden = false

        switch type {
        case .none:
            button?.isHidden = true
        case .dontSeeProvider:
            button?.setTitle("Don't see provider", for: .normal)
        case .showAllProviders:
            button?.setTitle("Show all providers", for: .normal)
        }
        })
        .disposed(by: bag)

    viewModel.items
        .bind(to: collectionView
        .rx
        .items(cellIdentifier: "ProviderCell", cellType: ProviderCollectionViewCell.self)) { [ unowned self ] _, item, cell in
            item.logoImage.bind(to: cell.imageView.rx.image).addDisposableTo(self.bag)
        }
        .addDisposableTo(bag)

    collectionView
        .rx
        .modelSelected(TopProvidersPickerItem.self)
        .bind(to: self.viewModel.selectedItem)
        .addDisposableTo(bag)

    viewAllProvidersButton
        .rx
        .tap
        .bind(to: self.viewModel.showAllProvidersTrigger)
        .addDisposableTo(bag)
}

}

I wrote a test for the "none" case, but haven't been able to figure out the other two cases:

import FBSnapshotTestCase
import OHHTTPStubs
import RxSwift
@testable import AuthSuite

class TopProvidersPickerViewControllerTests: FBSnapshotTestCase, 
ProvidersViewControllerTests {

override func setUp() {
    super.setUp()
    recordMode = true
}

func testDoesNotShowButtonWhenLoadingProviders() {
    let viewModel = TopProvidersPickerViewModel(items: .never())
    let controller = TopProvidersPickerViewController.instantiateViewController(with: viewModel)
    presentViewController(controller)

    FBSnapshotVerifyView(controller.view)
}
1

There are 1 best solutions below

4
On

I've never used FB Snapshot Tester. I'm going to have to look into that.

Here's how I would do it:

I wouldn't expose the enum to the ViewController. setupRx() would contain this instead:

private func setupRx() {

    viewModel.buttonTitle
        .bind(to: viewAllProvidersButton.rx.title(for: .normal))
        .disposed(by: bag)

    viewModel.buttonHidden
        .bind(to: viewAllProvidersButton.rx.isHidden)
        .disposed(by: bag)

    // everything else      
}

Then to test the title of the button, for example, I would use these tests:

import XCTest
import RxSwift
@testable import RxPlayground

class TopProvidersPickerViewModelTests: XCTestCase {

    func testButtonTitleEmptyItems() {
        let topProviders = Observable<[MVPD]>.just([])
        let decodableProviding = MockDecodableProviding()

        let viewModel = TopProvidersPickerViewModel(topProviders: topProviders, imageLoader: decodableProviding)

        var title: String = ""
        _ = viewModel.buttonTitle.subscribe(onNext: { title = $0 })

        XCTAssertEqual(title, "Don't see provider")
    }

    func testButtonTitle12Items() {
        let topProviders = Observable<[MVPD]>.just(Array(repeating: MVPD(), count: 12))
        let decodableProviding = MockDecodableProviding()

        let viewModel = TopProvidersPickerViewModel(topProviders: topProviders, imageLoader: decodableProviding)

        var title: String = ""
        _ = viewModel.buttonTitle.subscribe(onNext: { title = $0 })

        XCTAssertEqual(title, "Don't see provider")
    }

    func testButtonTitle13Items() {
        let topProviders = Observable<[MVPD]>.just(Array(repeating: MVPD(), count: 13))
        let decodableProviding = MockDecodableProviding()

        let viewModel = TopProvidersPickerViewModel(topProviders: topProviders, imageLoader: decodableProviding)

        var title: String = ""
        _ = viewModel.buttonTitle.subscribe(onNext: { title = $0 })

        XCTAssertEqual(title, "Show all providers")
    }
}


class MockDecodableProviding: DecodableProviding {
    // nothing needed for these tests.
}