A more complicated, but perhaps more future proof solution would be to use a seam. For more on seams, read https://qualitycoding.org/mocking-standalone-functions/. We can use this same technique to mock out initializers. First, add the following to your project:
import UIKit
let defaultUITableViewRowActionInit: (UITableViewRowAction.Style, String?, @escaping (UITableViewRowAction, IndexPath) -> Void) -> UIKit.UITableViewRowAction = { style, title, handler in
return UIKit.UITableViewRowAction(style: style, title: title, handler: handler)
}
var uiTableViewRowActionInit = defaultUITableViewRowActionInit
func UITableViewRowAction(style: UITableViewRowAction.Style, title: String?, handler: @escaping (UITableViewRowAction, IndexPath) -> Void) -> UIKit.UITableViewRowAction {
return uiTableViewRowActionInit(style, title, handler)
}
From here on out, calls to UITableVeiwRowAction will go through our function instead of normal initializer. During normal prod use, the behavior will be the same. For tests, we can now add a mock first:
class UITableViewRowActionMock: UITableViewRowAction {
var passedStyle: UITableViewRowAction.Style
var passedTitle: String?
var passedHandler: (UITableViewRowAction, IndexPath) -> Void
init(style: UITableViewRowAction.Style, title: String?, handler: @escaping (UITableViewRowAction, IndexPath) -> Void) {
passedStyle = style
passedTitle = title
passedHandler = handler
}
}
This class just captures the args passed to it, including the block. From here, we need our test setup and teardown to setup and teardown the seam:
override func setUp() {
super.setUp()
// CODE ...
uiTableViewRowActionInit = { style, title, handler in
return UITableViewRowActionMock(style: style, title: title, handler: handler)
}
}
override func tearDown() {
// CODE ...
uiTableViewRowActionInit = defaultUITableViewRowActionInit
super.tearDown()
}
And from there, we can run our test:
func testEditActionSomehow() {
let indexPath = IndexPath(row: 0, section: 0)
// here I just grab, first, feel free to do something more complicated
guard let action = sut.tableView(sut.tableView, editActionsForRowAt: indexPath)?.first as? UITableViewRowActionMock else {
XCTFail("Unexpected type")
return
}
action.passedHandler(action, indexPath)
//
Add assertion code here
}
If you have a lot of cases to test though, you may want to extract most of the code from your actions to somewhere more testable.