Skip to content

Commit ab18b73

Browse files
authoredNov 19, 2024
Expand the 'Known Issues' documentation article (#823)
This expands the [Known issues](https://swiftpackageindex.com/swiftlang/swift-testing/main/documentation/testing/known-issues) DocC article to give examples and describe several different ways it can be used. ### Checklist: - [x] Code and documentation should follow the style of the [Style Guide](https://github.com/apple/swift-testing/blob/main/Documentation/StyleGuide.md). - [x] If public symbols are renamed or modified, DocC references should be updated. Resolves rdar://137961874

File tree

2 files changed

+157
-8
lines changed

2 files changed

+157
-8
lines changed
 

‎Sources/Testing/Issues/KnownIssue.swift

+20-4
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public typealias KnownIssueMatcher = @Sendable (_ issue: Issue) -> Bool
9696
/// be attributed.
9797
/// - body: The function to invoke.
9898
///
99-
/// Use this function when a test is known to raise one or more issues that
99+
/// Use this function when a test is known to record one or more issues that
100100
/// should not cause the test to fail. For example:
101101
///
102102
/// ```swift
@@ -112,6 +112,10 @@ public typealias KnownIssueMatcher = @Sendable (_ issue: Issue) -> Bool
112112
/// while others should continue to cause test failures, use
113113
/// ``withKnownIssue(_:isIntermittent:sourceLocation:_:when:matching:)``
114114
/// instead.
115+
///
116+
/// ## See Also
117+
///
118+
/// - <doc:known-issues>
115119
public func withKnownIssue(
116120
_ comment: Comment? = nil,
117121
isIntermittent: Bool = false,
@@ -143,7 +147,7 @@ public func withKnownIssue(
143147
/// - Throws: Whatever is thrown by `body`, unless it is matched by
144148
/// `issueMatcher`.
145149
///
146-
/// Use this function when a test is known to raise one or more issues that
150+
/// Use this function when a test is known to record one or more issues that
147151
/// should not cause the test to fail, or if a precondition affects whether
148152
/// issues are known to occur. For example:
149153
///
@@ -165,6 +169,10 @@ public func withKnownIssue(
165169
/// instead.
166170
///
167171
/// - Note: `issueMatcher` may be invoked more than once for the same issue.
172+
///
173+
/// ## See Also
174+
///
175+
/// - <doc:known-issues>
168176
public func withKnownIssue(
169177
_ comment: Comment? = nil,
170178
isIntermittent: Bool = false,
@@ -205,7 +213,7 @@ public func withKnownIssue(
205213
/// be attributed.
206214
/// - body: The function to invoke.
207215
///
208-
/// Use this function when a test is known to raise one or more issues that
216+
/// Use this function when a test is known to record one or more issues that
209217
/// should not cause the test to fail. For example:
210218
///
211219
/// ```swift
@@ -221,6 +229,10 @@ public func withKnownIssue(
221229
/// while others should continue to cause test failures, use
222230
/// ``withKnownIssue(_:isIntermittent:isolation:sourceLocation:_:when:matching:)``
223231
/// instead.
232+
///
233+
/// ## See Also
234+
///
235+
/// - <doc:known-issues>
224236
public func withKnownIssue(
225237
_ comment: Comment? = nil,
226238
isIntermittent: Bool = false,
@@ -254,7 +266,7 @@ public func withKnownIssue(
254266
/// - Throws: Whatever is thrown by `body`, unless it is matched by
255267
/// `issueMatcher`.
256268
///
257-
/// Use this function when a test is known to raise one or more issues that
269+
/// Use this function when a test is known to record one or more issues that
258270
/// should not cause the test to fail, or if a precondition affects whether
259271
/// issues are known to occur. For example:
260272
///
@@ -276,6 +288,10 @@ public func withKnownIssue(
276288
/// instead.
277289
///
278290
/// - Note: `issueMatcher` may be invoked more than once for the same issue.
291+
///
292+
/// ## See Also
293+
///
294+
/// - <doc:known-issues>
279295
public func withKnownIssue(
280296
_ comment: Comment? = nil,
281297
isIntermittent: Bool = false,

‎Sources/Testing/Testing.docc/known-issues.md

+137-4
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,146 @@ See https://swift.org/LICENSE.txt for license information
1010
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
1111
-->
1212

13-
Highlight known issues when running tests.
13+
Mark issues as known when running tests.
1414

1515
## Overview
1616

17-
The testing library provides a function, `withKnownIssue()`, that you
18-
use to mark issues as known. Use this function to inform the testing library
19-
at runtime not to mark the test as failing when issues occur.
17+
The testing library provides several functions named `withKnownIssue()` that
18+
you can use to mark issues as known. Use them to inform the testing library that
19+
a test should not be marked as failing if only known issues are recorded.
20+
21+
### Mark an expectation failure as known
22+
23+
Consider a test function with a single expectation:
24+
25+
```swift
26+
@Test func grillHeating() throws {
27+
var foodTruck = FoodTruck()
28+
try foodTruck.startGrill()
29+
#expect(foodTruck.grill.isHeating) // ❌ Expectation failed
30+
}
31+
```
32+
33+
If the value of the `isHeating` property is `false`, `#expect` will record an
34+
issue. If you cannot fix the underlying problem, you can surround the failing
35+
code in a closure passed to `withKnownIssue()`:
36+
37+
```swift
38+
@Test func grillHeating() throws {
39+
var foodTruck = FoodTruck()
40+
try foodTruck.startGrill()
41+
withKnownIssue("Propane tank is empty") {
42+
#expect(foodTruck.grill.isHeating) // Known issue
43+
}
44+
}
45+
```
46+
47+
The issue recorded by `#expect` will then be considered "known" and the test
48+
will not be marked as a failure. You may include an optional comment to explain
49+
the problem or provide context.
50+
51+
### Mark a thrown error as known
52+
53+
If an `Error` is caught by the closure passed to `withKnownIssue()`, the issue
54+
representing that caught error will be marked as known. Continuing the previous
55+
example, suppose the problem is that the `startGrill()` function is throwing an
56+
error. You can apply `withKnownIssue()` to this situation as well:
57+
58+
```swift
59+
@Test func grillHeating() {
60+
var foodTruck = FoodTruck()
61+
withKnownIssue {
62+
try foodTruck.startGrill() // Known issue
63+
#expect(foodTruck.grill.isHeating)
64+
}
65+
}
66+
```
67+
68+
Because all errors thrown from the closure are caught and interpreted as known
69+
issues, the `withKnownIssue()` function is not throwing. Consequently, any
70+
subsequent code which depends on the throwing call having succeeded (such as the
71+
`#expect` after `startGrill()`) must be included in the closure to avoid
72+
additional issues.
73+
74+
- Note: `withKnownIssue()` is recommended instead of `#expect(throws:)` for any
75+
error which is considered a known issue so that the test status and results
76+
will reflect the situation more accurately.
77+
78+
### Match a specific issue
79+
80+
By default, `withKnownIssue()` considers all issues recorded while invoking the
81+
body closure known. If multiple issues may be recorded, you can pass a trailing
82+
closure labeled `matching:` which will be called once for each recorded issue
83+
to determine whether it should be treated as known:
84+
85+
```swift
86+
@Test func batteryLevel() throws {
87+
var foodTruck = FoodTruck()
88+
try withKnownIssue {
89+
let batteryLevel = try #require(foodTruck.batteryLevel) // Known
90+
#expect(batteryLevel >= 0.8) // Not considered known
91+
} matching: { issue in
92+
guard case .expectationFailed(let expectation) = issue.kind else {
93+
return false
94+
}
95+
return expectation.isRequired
96+
}
97+
}
98+
```
99+
100+
### Resolve a known issue
101+
102+
If there are no issues recorded while calling `function`, `withKnownIssue()`
103+
will record a distinct issue about the lack of any issues having been recorded.
104+
This notifies you that the underlying problem may have been resolved so that you
105+
can investigate and consider removing `withKnownIssue()` if it's no longer
106+
necessary.
107+
108+
### Handle a nondeterministic failure
109+
110+
If `withKnownIssue()` sometimes succeeds but other times records an issue
111+
indicating there were no known issues, this may indicate a nondeterministic
112+
failure or a "flaky" test.
113+
114+
The first step in resolving a nondeterministic test failure is to analyze the
115+
code being tested and determine the source of the unpredictable behavior. If
116+
you discover a bug such as a race condition, the ideal resolution is to fix
117+
the underlying problem so that the code always behaves consistently even if
118+
it continues to exhibit the known issue.
119+
120+
If the underlying problem only occurs in certain circumstances, consider
121+
including a precondition. For example, if the grill only fails to heat when
122+
there's no propane, you can pass a trailing closure labeled `when:` which
123+
determines whether issues recorded in the body closure should be considered
124+
known:
125+
126+
```swift
127+
@Test func grillHeating() throws {
128+
var foodTruck = FoodTruck()
129+
try foodTruck.startGrill()
130+
withKnownIssue {
131+
// Only considered known when hasPropane == false
132+
#expect(foodTruck.grill.isHeating)
133+
} when: {
134+
!hasPropane
135+
}
136+
}
137+
```
138+
139+
If the underlying problem is unpredictable and fails at random, you can pass
140+
`isIntermittent: true` to let the testing library know that it will not always
141+
occur. Then, the testing library will not record an issue when zero known issues
142+
are recorded:
143+
144+
```swift
145+
@Test func grillHeating() throws {
146+
var foodTruck = FoodTruck()
147+
try foodTruck.startGrill()
148+
withKnownIssue(isIntermittent: true) {
149+
#expect(foodTruck.grill.isHeating)
150+
}
151+
}
152+
```
20153

21154
## Topics
22155

0 commit comments

Comments
 (0)
Please sign in to comment.