emenegro
emenegro

Reputation: 6971

How to know XCTestExpectation current fulfillment count

In async tests is common, at least for me, to have to know the fulfilment count of an expectation to know how to assert.

For instance, a test to check this scenario:

  1. Fetch data
  2. After receiving first callback, assert if items are as expected
  3. Fetch next page of items
  4. Assert if items are as expected
    func testOne() {
        let expectData = expectation(description: "data")
        expectData.expectedFulfillmentCount = 2
        var expectDataFulfillmentCount = 0
        
        sut.data = {
            expectData.fulfill()
            expectDataFulfillmentCount += 1
            
            if expectDataFulfillmentCount == 1 {
                XCTAssertEqual(sut.numberOfItems, 3)
                sut.fetchNextPage()
            } else if expectDataFulfillmentCount == 2 {
                XCTAssertEqual(sut.numberOfItems, 6)
            }
        }

        sut.fetch()

        waitForExpectations(timeout: 0.1, handler: nil)
    }

I am not happy with this pattern. I know I can slightly change how I assert when having expectations, this way:

    func testTwo() {
        let expectFirstData = expectation(description: "firstData")
        let expectSecondData = expectation(description: "secondData")
        
        sut.data = {
            if sut.numberOfItems == 3 {
                expectFirstData.fulfill()
                sut.fetchNextPage()
            } else if sut.numberOfItems == 6 {
                expectSecondData.fulfill()
            }
        }

        sut.fetch()

        wait(for: [expectFirstData, expectSecondData], timeout: 0.1, enforceOrder: true)
    }

But I don't like this neither because then I am not asserting (there is no XCTAssert), I am just fulfilling expectations and then loosing the capacity to easily identify why and where the test failed. This pattern, though, is perfect for expectations over boolean values, like this one:

    func testThree() {
        let truePerformingOperationExpect = expectation(description: "truePerformingOperationExpect")
        let falsePerformingOperationExpect = expectation(description: "falsePerformingOperationExpect")
        
        sut.performingOperation = { fetching in
            if fetching {
                truePerformingOperationExpect.fulfill()
            } else {
                falsePerformingOperationExpect.fulfill()
            }
        }
        
        sut.fetch()

        wait(for: [truePerformingOperationExpect, falsePerformingOperationExpect], timeout: 0.1, enforceOrder: true)
    }

For me, this could be easily solved if I can get the current fulfilment count of an expectation, it would clean up the test a lot and I would have both of best worlds. Is it possible? Is there any other way of doing this?

Upvotes: 5

Views: 2660

Answers (1)

kanobius
kanobius

Reputation: 881

I ended up subclassing XCTestExpectation:

class CountedFulfillmentTestExpectation: XCTestExpectation {
    private(set) var currentFulfillmentCount: Int = 0

    override func fulfill() {
        currentFulfillmentCount += 1
        super.fulfill()
    }
}

Upvotes: 6

Related Questions