You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift单元测试网络请求遇API违规:多次调用XCTestExpectation fulfill

Fixing "API violation - multiple calls made to -[XCTestExpectation fulfill]" in Network Request Unit Tests

Hey there, let's break down why you're hitting this API violation - multiple calls made to -[XCTestExpectation fulfill] error and how to fix it.

First, the root cause is straightforward: your XCTestExpectation instance is being called with fulfill() more than once. Each expectation is designed to be satisfied exactly once, so multiple calls trigger this violation. In your network test scenario, this almost always ties back to either:

  • Accidental multiple fulfill() calls in your test closure, or
  • Your network layer's request method invoking the completion closure more than once.

Let's walk through concrete fixes and checks based on your code snippet:

1. Audit your test closure for duplicate fulfill() calls

Looking at your partial code, it's easy to accidentally place fulfill() in multiple nested if branches. For example, if you call it inside the json parsing block and in an error handling branch, that's two calls right there.

Bad Example (what you might have):

func testAccessKeys() { 
    let expected = expectation(description: "Run the Access request") 
    sut.request(.Access, data: nil) { finished, response in 
        if response != nil && finished == true { 
            if let json = response as? [String:Any] { 
                // Assert some values
                expected.fulfill() // First call
            }
        } else {
            // Handle failure
            expected.fulfill() // Second call
        }
    }
    waitForExpectations(timeout: 5, handler: nil)
}

Fixed Version:

Use defer to guarantee fulfill() runs exactly once, no matter which code path the closure takes:

func testAccessKeys() { 
    let expected = expectation(description: "Run the Access request") 
    sut.request(.Access, data: nil) { finished, response in 
        // Ensure fulfill is called once, even if assertions fail
        defer { expected.fulfill() }
        
        // Your assertion logic here
        guard finished, let response = response as? [String:Any] else {
            XCTFail("Request failed or returned invalid response")
            return
        }
        
        XCTAssertNotNil(response["access_key"], "Access key should be present")
        // Add other relevant assertions
    }
    waitForExpectations(timeout: 5) { error in
        if let error = error {
            XCTFail("Timed out waiting for access request: \(error.localizedDescription)")
        }
    }
}

2. Check your network layer's request implementation

If the test code looks clean, the issue might be in your sut (system under test) itself. Common culprits include:

  • The request method calls the completion closure on both success and error paths without proper guard clauses
  • Duplicate callback registrations (e.g., adding a listener multiple times for the same request)
  • Race conditions where network events trigger the closure more than once

Add print statements or breakpoints inside your request method to count how many times the completion closure is invoked. If it's more than once, you'll need to fix that logic to ensure the closure is called exactly once per request.

3. Enable strict over-fulfillment checking (optional)

To get clearer debug info, enable assertForOverFulfill on your expectation. This will throw a more specific error pointing to where the duplicate fulfill() is happening:

let expected = expectation(description: "Run the Access request")
expected.assertForOverFulfill = true // Enforce single fulfillment

Content的提问来源于stack exchange,提问作者user7684436

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:24:22