How would I write a test to make sure the UIbutton "Show all Providers" turns up when there's more than 12 or more items in the table view?

293 views Asked by At

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 answers

4
Daniel T. 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.
}