Hello I'm trying to unit testing a private function which located in Presenter
This is my Presenter Codes and I'm using Networking Singleton Object APIService
class MyPresenter {
weak var vc: MyProtocol?
func attachView(vc: MyProtocol?) {
self.vc = vc
}
func request(_ id: String) {
if id.count == 0 {
vc?.showIDEmptyAlert()
return
}
fetch(id)
}
private func fetch(_ id:String) {
DispatchQueue.global.async {
APIService.shared.fetch(id) { (data, err) in
if let err = err {
self.vc?.showErrorAlert()
return
}
self.vc?.update(data)
}
}
}
}
and this is my ViewController codes
class MyViewController: UIViewController, MyProtocol {
private var presenter: MyPresenter = MyPresenter()
override func viewDidLoad() {
super.viewDidLoad()
presenter.attachView(vc: self)
}
func showIDEmptyAlert() {
self.present ..
}
func showErrorAlert() {
self.present ..
}
func update(data: String) {
self.label.text = data
}
@IBAction func handleRegisterButton(_ sender: UIButton) {
guard let id = idTextField.text else { return }
presenter.request(id)
}
}
These are my Presenter and View. And I wrote Test Code like this
First, I made Mock PassiveView Like this
class MyViewMock: MyProtocol {
private (set) var showEmptyIdAlertHasBeenCalled = false
private (set) var showErrorAlertHasBeenCalled = false
private (set) var updateHasBeenCalled = false
func showEmptyIdAlert() {
showEmptyIdAlertHasBeenCalled = true
}
func showErrorAlert() {
showErrorAlertHasBeenCalled = true
}
func update(data: String) {
updateHasBeenCalled = true
}
}
So I expected that if I could test Presenter's request(_:)
methods with valid id and invalid
but because request(_:)
didn't get handler parameter and APIService.shared.fetch
is asynchronous, I couldn't get
correct result by calling request(_:)
. (Always false)
How can I test this kind of Presenter?
In terms ofXCTests
, there is the XCTestExpectation
class to test asynchronous functions.
But there is an issue in your approach of testing the Presenter. You should use mock for your network service and stub it with expected arguments. It doesn't make sense to call the actual service. Unit tests are the fastest kind of tests. Private methods are a part of black-box which you should not care about its internal structure. Test public interfaces but don't test private methods.
If you will try to mock and stub APIService
, then you notice it's impossible to do if it's a singleton. You'll end up with injecting service as a dependency into Presenter for the better testability.
If the service will be mocked and the stub will be used, then there is no need in using XCTestExpectation
, because there won't be any asynchronous code.