You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* create your publisher & relevant pipeline if so desired
21
-
* create a sink to capture the results that works on both completions and values
22
-
** this can be separate, or just chained to the pipeline, depending on what makes most sense to you
23
-
* `wait` on the expectation to let the test "do it's thing" in the background
24
+
When you are testing a publisher, or something that creates a publisher, you may not have the option of controlling when the publisher returns data for your tests.
25
+
Combine, being driven by its subscribers, can set up a sync that initiates the data flow.
26
+
You can use an https://developer.apple.com/documentation/xctest/xctestexpectation[XCTestExpectation] to wait an explicit amount of time for the test to run to completion.
27
+
28
+
A general pattern for using this with combine includes:
29
+
30
+
. set up the expectation within the test
31
+
. establish the code you are going to test
32
+
. set up the code to be invoked such that on the success path you call the expectation's `.fulfill()` function
33
+
. set up a `wait()` function with an explicit timeout that will fail the test if the expectation isn't fulfilled within that time window.
34
+
35
+
If you are testing the data results from a pipeline, then triggering the `fulfill()` function within the <<reference.adoc#reference-sink>> operator `receiveValue` closure can be very convenient.
36
+
If you are testing a failure condition from the pipeline, then often including `fulfill()` within the <<reference.adoc#reference-sink>> operator `receiveCompletion` closure is effective.
37
+
38
+
The following example shows testing a one-shot publisher (dataTaskPublisher in this case) using expectation, and expecting the data to flow without an error.
let expectation = XCTestExpectation(description: "Download from \(String(describing: testURL))") <1>
46
+
let remoteDataPublisher = URLSession.shared.dataTaskPublisher(for: self.testURL!)
47
+
// validate
48
+
.sink(receiveCompletion: { fini in
49
+
print(".sink() received the completion", String(describing: fini))
50
+
switch fini {
51
+
case .finished: expectation.fulfill() <2>
52
+
case .failure: XCTFail() <3>
53
+
}
54
+
}, receiveValue: { (data, response) in
55
+
guard let httpResponse = response as? HTTPURLResponse else {
56
+
XCTFail("Unable to parse response an HTTPURLResponse")
57
+
return
58
+
}
59
+
XCTAssertNotNil(data)
60
+
// print(".sink() data received \(data)")
61
+
XCTAssertNotNil(httpResponse)
62
+
XCTAssertEqual(httpResponse.statusCode, 200) <4>
63
+
// print(".sink() httpResponse received \(httpResponse)")
64
+
})
65
+
66
+
XCTAssertNotNil(remoteDataPublisher)
67
+
wait(for: [expectation], timeout: 5.0) <5>
68
+
}
69
+
----
70
+
71
+
<1> The expectation is set up with a string that makes debugging in the event of failure a bit easier.
72
+
This string is really only seen when a test failure occurs.
73
+
The code we are testing here is dataTaskPublisher retrieving data from a preset test URL, defined earlier in the test.
74
+
The publisher is invoked by attaching the <<reference.adoc#reference-sink>> subscriber to it.
75
+
Without the expectation, the code will still run, but the test running structure wouldn't wait to see if there were any exceptions.
76
+
The expectation within the test "holds the test" waiting for a response to let the operators do their work.
77
+
<2> In this case, the test is expected to complete successfully and terminate normally, therefore thethe `expectation.fulfill()` invocation is set within the receiveCompletion closure, specifically linked to a received `.finished` completion.
78
+
<3> Since we don't expect a failure, we also have an explicit XCTFail() invocation if we receive a `.failure` completion.
79
+
<4> We have a few additional assertions within the receiveValue.
80
+
Since this publisher set returns a single value and then terminates, we can make easily make inline assertions about the data received.
81
+
If we received multiple values, then we could collect those and make assertions on what was received after the fact.
82
+
<5> This test uses a single expectation, but you can include multiple independent expectations to require fulfillment.
83
+
It also sets that maximum time that this test can run to five seconds.
84
+
The test will not always take five seconds, as it will complete the test as soon as the fulfill is received.
24
85
25
86
// force a page break - in HTML rendering is just a <HR>
0 commit comments