1

I'm trying to write some UnitTests for the first time. My pattern is MVP and I'm trying to test my Presenter. I've created mock class: class TeamViewMock: TeamViewPresenterProtocol { }. It contains all the methods from my real Presenter. Inside the each method I'm trying to set the new value for the property, so when the method called - property should get a new value. Only one property gets new value out of 4 and I've no clue why the other ones didn't get it. You may see it in the following code

import XCTest
@testable import NHL
 
class TeamViewPresenterTest: XCTestCase {
    
    var presenter: TeamViewPresenter!
    var viewMock: TeamViewMock!
    
    func setupPresenter() {
        viewMock = TeamViewMock()
        presenter = TeamViewPresenter(with: viewMock)
    }
    
    func testGetData() {
        setupPresenter()
        presenter.getData(completion: {_ in })
        
        XCTAssertTrue(viewMock.isStart) // This one works and returns true
        XCTAssertTrue(viewMock.isStop) // Return error
        XCTAssertTrue(viewMock.isEndRefreshing) // Return error
        XCTAssertTrue(viewMock.isReload) // Return error
    }
}

class TeamViewMock: TeamViewPresenterProtocol {
    var isStart = false
    var isStop = false
    var isEndRefreshing = false
    var isReload = false
    
    func startAnimating() {
        self.isStart = true // Testing stops here and doesn't go any further...
    }
    func stopAnimating() {
        self.isStop = true
    }
    func endRefreshing() {
        self.isEndRefreshing = true
    }
    func reloadView(_ teams: NHLDTO) {
        self.isReload = true
    }
}

class TeamViewPresenter {
    
    // MARK: - Public Properties
    
    private weak var view: TeamViewPresenterProtocol?
    public let dataFetcherService = DataFetcherService()
    
    // MARK: - Initializers
    
    init(with view: TeamViewPresenterProtocol) {
        self.view = view
    }
    
    // MARK: - Public Methods
    
    public func getData(completion: @escaping (AppError) -> Void) {
        view?.startAnimating() // Testing stops here and doesn't go any further, but still returns true for the property isStart and error for the rest
        dataFetcherService.fetchTeamData { [weak self] result in
            guard let self = self else { return }
            switch result {
            case .failure(let error):
                completion(error)
                print(error)
            case .success(let teams):
                guard let teams = teams else { return }
                self.view?.reloadView(teams)
                self.view?.stopAnimating()
                self.view?.endRefreshing()
            }
        }
    }  
}

protocol TeamViewPresenterProtocol: AnyObject {
    func startAnimating()
    func stopAnimating()
    func reloadView(_ teams: NHLDTO)
    func endRefreshing()
}
matt
  • 515,959
  • 87
  • 875
  • 1,141
  • You are calling an async function in getData, this is probably causing the problems. I assume you need to do tests (XCTAssert...) inside the closure – Joakim Danielson Dec 29 '21 at 15:33
  • MVP pattern is _eminently_ testable; that is why it exists, in large measure. The problem here appears to be mainly that you are not mocking DataFetcherService. Remember, only test the system under test, and never involve _real_ externalities such as the network. – matt Dec 29 '21 at 15:59
  • @matt Thank you for your reply. So, if I got you right, I should forget about testing my getData() method, because it's linked with networking? – Nikolai Borisov Dec 30 '21 at 05:43
  • @JoakimDanielson Thank you for your reply. It seems like I shouldn't be testing my networking method getData() at all... – Nikolai Borisov Dec 30 '21 at 05:45
  • There are two issues. One is that you need to mock DataFetcherService so that it doesn't network. The other is that testing async material requires async testing. – matt Dec 30 '21 at 06:27

0 Answers0