CxTest
A collection of test helpers for Combine
Testing Combine can be cumbersome. If you need to test any logic to Publishers you create or expose, one of the only options is to throw logic into a Sink
:
func testPublisherFailsWithSpecifiedError() {
let failing = Fail(outputType: Void.self, failure: SomeError())
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
XCTFail("Did not fail with an error")
case .failure:
return
}
}, receiveValue: { output in
XCTFail("Should not have received value: \(output)")
})
}
With CxTest, the previous example is simplified to this:
let failing = Fail(outputType: Void.self, failure: SomeError())
.assertFailure()
When you’re creating your own Combine publishers or operators, testing can normally be done using something like a PassthroughSubject
to avoid having to actually call an asynchrounous API:
func testValuesAreLessThan10() {
let passthrough = PassthroughSubject<Int, Never>()
let clamped = passthrough
.clamp(range: Int.min..<10)
.assert(lessThan: 10)
passthrough.send(20)
passthrough.send(40)
passthrough.send(completion: .finished)
}
CxTest also has full support for asynchronous APIs:
func testValuesAreLessThan10Async() {
let clampedExpectation = XCTestExpectation(description: "clamped")
let clamped = AsyncNumberPublisher()
.clamp(range: Int.min..<10)
.assert(lessThan: 10, expectation: clampedExpectation)
wait(for: [clampedExpectation], timeout: 5)
}
CxTest has a comparable operator for every XCTest assertion with the exception of XCTAssertThrowsError
and XCTAssertNoThrow
. These error throwing assertions are replaced by assertFailure()
, assertError(type:expectation:)
, and assertError(equals:expectation:)
operators.