Declan McKenna
Declan McKenna

Reputation: 4870

Cannot set value conforming to protocol to property with protocol type

I'm trying to create a fake authenticator for my unit tests that can manually set the user as logged in or logged out and bypass the API my code actually uses where I'd need a real accessToken to log the user in.

I've wrapped the Authentication API my app uses in to the following class:

API Wrapper

import OIDC

protocol Authenticator {
    func getValidAccessToken(completionHandler: @escaping (Error?, String?) -> Void)
}

struct OIDCAuthenticator: Authenticator {

    func getValidAccessToken(completionHandler: @escaping (Error?, String?) -> Void) {
        //API call
        OIDCHelper.getValidAccessToken { (error, accessToken) in
            DispatchQueue.main.async {
                completionHandler( error, accessToken)
            }
        }
    }
}

Then I create a Fake Authenticator using the same protocol for testing purposes

Fake/Testing Authenticator

import OIDC
import Foundation

///Mocks the user being logged in our logged out for Testing purposes
struct FakeAuthenticator: Authenticator {
    let error: OIDCError?
    let accessToken: String?

    func getValidAccessToken(completionHandler: @escaping (Error?, String?) -> Void) {
        completionHandler(error, accessToken)
    }

    init(loggedIn: Bool) {
        if loggedIn {
            error = .tokenNotFoundInKeychainData
            accessToken = nil
        } else {
            error = nil
            accessToken = "abcdefg"
        }
    }
}

Settings the OIDCAuthenticator API Wrapper works fine when settings the authenticator in the ViewController subclass.

TableViewController Implementation

import UIKit
import OIDC

class SettingsPageTableViewController: UITableViewController{

    // MARK: - Outlets and variables

    var authenticator: Authenticator!
    private var isUserLoggedIn = false

    // MARK: - LifeCycle

    override func viewDidLoad() {
        super.viewDidLoad()
        authenticator = OIDCAuthenticator()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        authenticator.getValidAccessToken { [weak self] (error, _) in
            self?.isUserLoggedIn = (error == nil)
            self?.setLogginStatus()
        }
    }
}

However when I try to do the same thing with the FakeAuthenticator in my Unit tests I get the following error: enter image description here

SettingsViewController Test Class

import XCTest
import UIKit
@testable import MyProject

class SettingsViewControllerTests: XCTestCase {

    var viewController: SettingsPageTableViewController!

    override func setUp() {
        super.setUp()
        configureViewControllerForTesting()
    }

    private func configureViewControllerForTesting() {
        let storyboard = UIStoryboard(name: "SettingsPage", bundle: nil)
        let navigationController = storyboard.instantiateInitialViewController() as! UINavigationController
        viewController = navigationController.topViewController as! SettingsPageTableViewController
        _ = viewController.view
    }

    func testSignInButtonIsAvailableWhenUnauthenticated() {
        viewController.authenticator = FakeAuthenticator(loggedIn: false)
    }
}

The same things happens when I swap out FakeAuthenticator with OIDCAuthenticator. I've also attempted to cast the FakeAuthenticator to Authenticator but this merely alters the Error to Cannot assign value of type 'Authenticator' to type 'Authenticator!'.

Why am I getting this error and what is the best approach to fixing this?

Upvotes: 1

Views: 157

Answers (1)

u84six
u84six

Reputation: 4941

You need to remove the files from your test target since you're already importing your whole project with @testable.

Upvotes: 2

Related Questions