ios – Unit testing an async function in a loop gives an error of multiple calls made to -[XCTestExpectation fulfill]


I wrote a unit test to test if my email validation regex works. This works well if I check one email address string in my test. This is my code which works:

func testValidEmail_showsAlertWithSuccessMessage_whenLoginButtonIsTapped() throws {
    // Given: Login view model configured with the mock email regex specified in the appConfigResponse.json
    let appConfigService = try XCTUnwrap(createDefaultMockConfigService())
    
    // Given: A list of valid email addresses
    let validEmails = generateValidEmails()
    
    let waitExpectation = expectation(description: "testValidEmail_showsAlertWithSuccessMessage_whenLoginButtonIsTapped")
    
    appConfigService.loadAppConfig { [weak self] _ in
        guard let self else { return }
        
        let appInfoManager = AppInfoManager(service: appConfigService)
        let viewModel = LoginViewModel(appInfoManager: appInfoManager)
        
        viewModel.$shouldShowEmailValidationAlert
            .drop(while: { $0 == false })
            .sink { _ in
                // Then: Verify the success message is displayed in the alert
                XCTAssertEqual(viewModel.emailValidationAlertText,
                               LoginViewModel.StringConstants.validEmailTitle)
                
                waitExpectation.fulfill()
            }
            .store(in: &self.cancellables)
        
        // When: Entering a valid email address and then tapping on the login button
        viewModel.emailFieldViewModel.text = validEmails.randomElement()!
        viewModel.didTapLoginButton()
    }
    
    waitForExpectations()
}

However, I have an array of email addresses to verify against and if I add a loop, I get the error: API violation - multiple calls made to -[XCTestExpectation fulfill]

I understand that this is due to the expectation being called more than specified.

I took some advice from this SO answer and added this code:

let waitExpectation = expectation(description: "testValidEmail_showsAlertWithSuccessMessage_whenLoginButtonIsTapped")
waitExpectation.expectedFulfillmentCount = validEmails.count

However, this still gives me the same error. This is my code with the loop

func testValidEmail_showsAlertWithSuccessMessage_whenLoginButtonIsTapped() throws {
    // Given: Login view model configured with the mock email regex specified in the appConfigResponse.json
    let appConfigService = try XCTUnwrap(createDefaultMockConfigService())
    
    // Given: A list of valid email addresses
    let validEmails = generateValidEmails()
    
    let waitExpectation = expectation(description: "testValidEmail_showsAlertWithSuccessMessage_whenLoginButtonIsTapped")
    waitExpectation.expectedFulfillmentCount = validEmails.count
    
    appConfigService.loadAppConfig { [weak self] _ in
        guard let self else { return }
        
        let appInfoManager = AppInfoManager(service: appConfigService)
        let viewModel = LoginViewModel(appInfoManager: appInfoManager)
        
        for validEmail in validEmails {
            viewModel.$shouldShowEmailValidationAlert
                .drop(while: { $0 == false })
                .sink { _ in
                    // Then: Verify the success message is displayed in the alert
                    XCTAssertEqual(viewModel.emailValidationAlertText,
                                   LoginViewModel.StringConstants.validEmailTitle)
                    
                    waitExpectation.fulfill()
                }
                .store(in: &self.cancellables)
            
            // When: Entering a valid email address and then tapping on the login button
            viewModel.emailFieldViewModel.text = validEmail
            viewModel.didTapLoginButton()
        }
    }
    
    waitForExpectations()
}

Latest articles

spot_imgspot_img

Related articles

Leave a reply

Please enter your comment!
Please enter your name here

spot_imgspot_img