kid_x
kid_x

Reputation: 1475

Testing UITableView.rx.itemSelected callback in RxSwift

I have a tableView bound to instance var myStrings: BehaviorRelay<[String]>, such that the label of each cell in the tableView is set to the value of each string in myStrings:

myStrings.bind(to: tableView.rx.items(cellIdentifier: cellReuseId, cellType: MyTableCell.self)) { row, str, cell in
            cell.textLabel?.text = str
        }.disposed(by: disposeBag)

I'm subscribed to item selection on the tableView.

tableView.rx.itemSelected.subscribe(onNext: { indexPath in
            let currentStr: String = try! self.tableView.rx.model(at: indexPath)
            self.delegate?.use(currentStr)
        }).disposed(by: disposeBag)

How would I go about testing the closure I have on my subscription to itemsSelected in a unit test?

Upvotes: 1

Views: 3218

Answers (1)

Evan R
Evan R

Reputation: 875

Not sure why you'd need to use delegation if you're using RxSwift, but one way to test what you're after is to inject your table view into the object that uses it in your code snippet, and to also create a mock object that conforms to your table view cell's delegate (the one that's in your subscription that you want to test). The former will allow you to create a table view in your unit test case and to programmatically select a cell in it. The latter will then allow you to test that the delegate method called in your subscription is being called with the correct string.

To test your delegate, use a mock object that conforms to your delegate's protocol in your unit test, set up a "spy" for your method, and then run assertions against that object and its spy callback:

class MockDelegateObject: TableViewCellDelegate {
    var stubUse: (() -> String)?

    // MARK: - TableViewCellDelegate
    func use(_ cellString: String) {
        stubUse?(cellString)
    }
}

class MyTableViewTests: XCTestCase {
    func testCellString() {
        let testExpectation = expectation(description: #function)

        let expectedCurrentString = "Foo"

        // Create an instance of your mock object which conforms to your delegate; its spy method will get called with your cell text string when the delegate method is called in your subscription code
        let mockDelegateObject = MockDelegateObject()
        mockDelegateObject.stubUse = { cellString in
            XCTAssertEqual(expectedCurrentString, cellString)
            testExpectation.fulfill()
        }

        // Initialize your table view and do whatever you need to do to add your table view to a window, view controller, etc.
        let tableView = MyTableView()
        let sut = MyTableViewOwningObject(tableView: tableView)

        // Set your object's delegate to be the mock delegate we've created which will receive messages from the subscription in your code
        sut.delegate = mockDelegateObject

        // Do whatever it is you need to do to kick off your table view data loading/displaying, ultimately calling
        sut.loadData()

        // Tell your table view to select whichever cell you want
        let firstIndexPath = IndexPath(row: 0, section: 0)
        tableView.selectRow(at: firstIndexPath, animated: false, scrollPosition: .none)
        tableView.delegate?.tableView?(self.tableView, didSelectRowAt: firstIndexPath)

        waitForExpectations(timeout: 0.001)
    }
}

Upvotes: 0

Related Questions