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
Introduce GInkgoHelper() to track and exclude helper functions from porential CodeLcoations
Similar to testing.T.Helper(), GinkgoHelper() makes it easier to write and nest helper functions without having to manage offsets. This commit also updates GinkgoT() to wire up GinkgoT().Helper() to GinkgoHelper()
Copy file name to clipboardexpand all lines: core_dsl.go
+9
Original file line number
Diff line number
Diff line change
@@ -163,6 +163,15 @@ func GinkgoParallelProcess() int {
163
163
returnsuiteConfig.ParallelProcess
164
164
}
165
165
166
+
/*
167
+
GinkgoHelper marks the function it's called in as a test helper. When a failure occurs inside a helper function, Ginkgo will skip the helper when analyzing the stack trace to identify where the failure occurred.
168
+
169
+
This is an alternative, simpler, mechanism to passing in a skip offset when calling Fail or using Gomega.
170
+
*/
171
+
funcGinkgoHelper() {
172
+
types.MarkAsHelper(1)
173
+
}
174
+
166
175
/*
167
176
PauseOutputInterception() pauses Ginkgo's output interception. This is only relevant
168
177
when running in parallel and output to stdout/stderr is being intercepted. You generally
Copy file name to clipboardexpand all lines: docs/index.md
+79
Original file line number
Diff line number
Diff line change
@@ -1202,6 +1202,66 @@ You must remember to follow this pattern when making assertions in goroutines -
1202
1202
1203
1203
When a failure occurs Ginkgo marks the current spec as failed and moves on to the next spec. If, however, you'd like to stop the entire suite when the first failure occurs you can run `ginkgo --fail-fast`.
1204
1204
1205
+
One last thing before we move on. When a failure occurs, Ginkgo records and presents the location of the failure to help you pinpoint where to look to debug your specs. This is typically the line where the call to `Fail` was performed (or, if you're using Gomega, the line where the Gomega assertion failed). Sometimes, however, you need to control the reported location. For example, consider the case where you are using a helper function:
1206
+
1207
+
```go
1208
+
/* === INVALID === */
1209
+
funcEnsureUserCanRead(bookBook, userUser) {
1210
+
if book.Title == "Les Miserables" && user.Age <= 3 {
1211
+
Fail("user is too young for this book") //A
1212
+
}
1213
+
}
1214
+
1215
+
It("can read books", func() {
1216
+
EnsureUserCanRead(book, user) //B
1217
+
user.Read(book)
1218
+
})
1219
+
```
1220
+
1221
+
Now, if the `EnsureUserCanRead` helper fails the location presented to the user will point to `//A`. Ideally, however we'd prefer that Ginkgo report `//B`.
1222
+
1223
+
There are a few ways to solve for this. The first is to pass `Fail` an `offset` like so:
1224
+
1225
+
```go
1226
+
funcEnsureUserCanRead(bookBook, userUser) {
1227
+
if book.Title == "Les Miserables" && user.Age <= 3 {
1228
+
Fail("user is too young for this book", 1)
1229
+
}
1230
+
}
1231
+
```
1232
+
1233
+
This will tell Ginkgo to skip a stack frame when calculating the offset. In this particular case Ginkgo will report the location that called `EnsureUserCanRead`: i.e. `//B`.
1234
+
1235
+
This works... however managing offset can quickly get unwieldy. For example, say we wanted to compose helpers:
1236
+
1237
+
```go
1238
+
funcEnsureUserCanCheckout(bookBook, userUser) {
1239
+
EnsureUserCanRead(book, user)
1240
+
EnsureUserHasAccessTo(book, user)
1241
+
}
1242
+
```
1243
+
1244
+
in _this_ case, we'd need the offset that `EnsureUserCanRead` passes to `Fail` to be `2` instead of `1`.
1245
+
1246
+
Instead of managing offsets you can use `GinkgoHelper()`:
1247
+
1248
+
```go
1249
+
funcEnsureUserCanRead(bookBook, userUser) {
1250
+
GinkgoHelper()
1251
+
if book.Title == "Les Miserables" && user.Age <= 3 {
1252
+
Fail("user is too young for this book") //note the optional offset is gone
1253
+
}
1254
+
}
1255
+
1256
+
funcEnsureUserCanCheckout(bookBook, userUser) {
1257
+
GinkgoHelper()
1258
+
EnsureUserCanRead(book, user)
1259
+
EnsureUserHasAccessTo(book, user)
1260
+
}
1261
+
```
1262
+
1263
+
Any function in which `GinkgoHelper()` is called is tracked by Ginkgo and ignored when a failure location is being computed. This allows you to build reusable test helpers and trust that the location presented to the user will always be in the spec that called the helper, and not the helper itself.
1264
+
1205
1265
### Logging Output
1206
1266
As outlined above, when a spec fails - say via a failed Gomega assertion - Ginkgo will pass the failure message passed to the `Fail` handler. Often times the failure message generated by Gomega gives you enough information to understand and resolve the spec failure.
1207
1267
@@ -4896,6 +4956,25 @@ now, if the `It` defined in `SharedBehaviorIt` the location reported by Ginkgo w
4896
4956
4897
4957
If multiple `Offset`s are provided on a given node, only the last one is used.
4898
4958
4959
+
Lastly, since introducing `Offset` Ginkgo has introduced `GinkgoHelper()` which marks the current functionas a test helper who's location should be skipped when determining the location for a node. We generally recommend using `GinkgoHelper()` instead of `Offset()` to manage how locations are computed. The above example could be rewritten as
4960
+
4961
+
```go
4962
+
SharedBehaviorIt := func() {
4963
+
GinkgoHelper()
4964
+
It("does something common and complicated", func() {
4965
+
...
4966
+
})
4967
+
}
4968
+
4969
+
Describe("thing A", func() {
4970
+
SharedBehaviorIt()
4971
+
})
4972
+
4973
+
Describe("thing B", func() {
4974
+
SharedBehaviorIt()
4975
+
})
4976
+
```
4977
+
4899
4978
#### The CodeLocation Decorator
4900
4979
In addition to `Offset`, users can decorate nodes with a `types.CodeLocation`. `CodeLocation`s are the structs Ginkgo uses to capture location information. You can, for example, set a custom location using `types.NewCustomCodeLocation(message string)`. Now when the location of the node is emitted the passed in `message` will be printed out instead of the usual `file:line` location.
Ω(output).Should(ContainSubstring(`fail_fixture_test.go:54`), "the code location reported for the helper failure - we're testing the call to GinkgoHelper() works as expected")
Copy file name to clipboardexpand all lines: table_dsl.go
+20-13
Original file line number
Diff line number
Diff line change
@@ -13,7 +13,7 @@ import (
13
13
/*
14
14
The EntryDescription decorator allows you to pass a format string to DescribeTable() and Entry(). This format string is used to generate entry names via:
15
15
16
-
fmt.Sprintf(formatString, parameters...)
16
+
fmt.Sprintf(formatString, parameters...)
17
17
18
18
where parameters are the parameters passed into the entry.
19
19
@@ -32,19 +32,20 @@ DescribeTable describes a table-driven spec.
32
32
33
33
For example:
34
34
35
-
DescribeTable("a simple table",
36
-
func(x int, y int, expected bool) {
37
-
Ω(x > y).Should(Equal(expected))
38
-
},
39
-
Entry("x > y", 1, 0, true),
40
-
Entry("x == y", 0, 0, false),
41
-
Entry("x < y", 0, 1, false),
42
-
)
35
+
DescribeTable("a simple table",
36
+
func(x int, y int, expected bool) {
37
+
Ω(x > y).Should(Equal(expected))
38
+
},
39
+
Entry("x > y", 1, 0, true),
40
+
Entry("x == y", 0, 0, false),
41
+
Entry("x < y", 0, 1, false),
42
+
)
43
43
44
44
You can learn more about DescribeTable here: https://onsi.github.io/ginkgo/#table-specs
45
45
And can explore some Table patterns here: https://onsi.github.io/ginkgo/#table-specs-patterns
// MarkAsHelper is used by GinkgoHelper to mark the caller (appropriately offset by skip)as a helper. You can use this directly if you need to provide an optional `skip` to mark functions further up the call stack as helpers.
0 commit comments