Search code examples
swiftunit-testingtddxctestcase

How can I verify a class method is called using XCTAssert?


I have a service class, I would like to assert 2 things

  1. A method is called
  2. The correct params are passed to that method

Here is my class

protocol OAuthServiceProtocol {
    func initAuthCodeFlow() -> Void
     func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) -> Void
}

class OAuthService: OAuthServiceProtocol {

    fileprivate let apiClient: APIClient

    init(apiClient: APIClient) {
        self.apiClient = apiClient
    }

    func initAuthCodeFlow() -> Void {

    }

    func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) -> Void {

    }
}

Here are my tests

class OAuthServiceTests: XCTestCase {
    var mockAPIClient: APIClient!
    var mockURLSession: MockURLSession!
    var sut: OAuthService!

    override func setUp() {
        mockAPIClient = APIClient()
        mockAPIClient.session = MockURLSession(data: nil, urlResponse: nil, error: nil)
        sut = OAuthService(apiClient: mockAPIClient)
    }

    func test_InitAuthCodeFlow_CallsRenderOAuthWebView() {
        let renderOAuthWebViewExpectation = expectation(description: "RenderOAuthWebView")

        class OAuthServiceMock: OAuthService {
            override func initAuthCodeFlow() -> Void {

            }

            override func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) {
                renderOAuthWebViewExpectation.fulfill()
            }
        }
    }
}

I was hoping to create a local sub class of OAuthService, assign that as my sut and call something like like sut.initAuthCodeFlow() and then assert that my expectation was fulfilled.

I believe this should satisfy point 1. However I cannot access my expectation when attempting to assign it as fulfilled as I get the following error

Class declaration cannot close over value 'renderOAuthWebViewExpectation' defined in outer scope

How can I mark this as fulfilled?

I am following a TDD approach, so I understand my OAuthService would produce a failing test at this point anyway*


Solution

  • Create a property on your mock, mutating it's value within the method you expect to call. You can then use your XCTAssertEqual to check that prop has been updated.

       func test_InitAuthCodeFlow_CallsRenderOAuthWebView() {
            let renderOAuthWebViewExpectation = expectation(description: "RenderOAuthWebView")
    
            class OAuthServiceMock: OAuthService {
                var renderOAuthWebViewExpectation: XCTestExpectation!
                var didCallRenderOAuthWebView = false
    
                override func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) {
                    didCallRenderOAuthWebView = true
                    renderOAuthWebViewExpectation.fulfill()
                }
            }
    
            let sut = OAuthServiceMock(apiClient: mockAPIClient)
    
            XCTAssertEqual(sut.didCallRenderOAuthWebView, false)
            sut.renderOAuthWebViewExpectation = renderOAuthWebViewExpectation
    
            sut.initAuthCodeFlow()
            waitForExpectations(timeout: 1) { _ in
                XCTAssertEqual(sut.didCallRenderOAuthWebView, true)
            }
    
        }