11# go-testpredicate
22
3- Test assertions library using predicate-like syntax, producing extensive
4- diagnostics output
3+ Test assertions library using a test predicate style syntax, and producing
4+ extensive diagnostics output
55
66[ ![ Latest] (
77 https://img.shields.io/github/v/tag/maargenton/go-testpredicate?color=blue&label=latest&logo=go&logoColor=white&sort=semver )] (
@@ -19,9 +19,10 @@ diagnostics output
1919
2020---------------------------
2121
22- Package ` go-testpredicate ` is a test assertions library exposing a
23- predicate-like syntax that works with Go testing support to provide extensive
24- diagnostics output and reduces the need to use a debugger on every failing test.
22+ Package ` go-testpredicate ` is a test assertions library exposing a test
23+ predicate style syntax for use with the built-in Go ` testing ` package, producing
24+ extensive diagnostics output and reducing the need to use a debugger on every
25+ failing test.
2526
2627The library contains an extensive collection of built-in predicates covering:
2728
@@ -33,29 +34,40 @@ The library contains an extensive collection of built-in predicates covering:
3334- set conditions on unordered collections
3435- panic conditions on code fragment execution
3536
37+ It also includes a BDD-style bifurcated evaluation context, where each test
38+ section is potentially evaluated multiple times in order to evaluate each branch
39+ independently.
40+
3641
3742## Installation
3843
39- go get github.com/maargenton/go-testpredicate
44+ ```
45+ go get github.com/maargenton/go-testpredicate
46+ ```
47+
48+ Optionally, you can add predefined code snippets for your text editor or IDE to
49+ assist in writing your test code. Snippets for VSCode are available
50+ [ here] ( docs/snippets.md )
4051
4152## Usage
4253
4354``` go
44- package examples_test
55+ package example_test
4556
4657import (
4758 " testing"
4859
60+ " github.com/maargenton/go-testpredicate/pkg/bdd"
4961 " github.com/maargenton/go-testpredicate/pkg/require"
5062 " github.com/maargenton/go-testpredicate/pkg/verify"
5163)
5264
5365func TestExample (t *testing .T ) {
54- t. Run ( " Given " , func (t *testing .T ) {
66+ bdd. Given (t, " something " , func (t *bdd .T ) {
5567 require.That (t, 123 ).ToString ().Length ().Eq (3 )
5668
57- t.Run ( " when " , func (t *testing .T ) {
58- t.Run ( " then " , func (t *testing .T ) {
69+ t.When ( " doing something " , func (t *bdd .T ) {
70+ t.Then ( " something happens " , func (t *bdd .T ) {
5971 verify.That (t, " 123" ).Eq (123 )
6072 verify.That (t, 123 ).ToString ().Length ().Eq (4 )
6173 })
@@ -67,14 +79,14 @@ func TestExample(t *testing.T) {
6779Output:
6880```
6981--- FAIL: TestExample (0.00s)
70- --- FAIL: TestFoo/Given_ (0.00s)
71- --- FAIL: TestFoo/Given_/when_ (0.00s)
72- --- FAIL: TestFoo/Given_/when_/then_ (0.00s)
73- usage_test .go:16 :
82+ --- FAIL: TestExample/Given_something (0.00s)
83+ --- FAIL: TestExample/Given_something/when_doing_something (0.00s)
84+ --- FAIL: TestExample/Given_something/when_doing_something/then_something_happens_ (0.00s)
85+ example_test .go:17 :
7486 expected: value == 123
7587 error: values of type 'string' and 'int' are never equal
7688 value: "123"
77- usage_test .go:17 :
89+ example_test .go:18 :
7890 expected: length(value.String()) == 4
7991 value: 123
8092 string: "123"
@@ -206,3 +218,121 @@ func TestStringAPI(t *testing.T) {
206218 verify.That (t, " aBc" ).ToUpper ().Eq (" ABC" )
207219}
208220```
221+
222+ ## BDD-style bifurcated tests
223+
224+ ### Rationale
225+
226+ First of all, the Go ` testing ` package is great and the fact that it is
227+ standard, built in and integrated with the Go tooling infrastructure is awesome.
228+ This is why the ` go-testpredicate ` packages strives to enhance it instead of
229+ replacing it, unlike many other testing packages.
230+
231+ If you look at other unit-testing packages, in other languages, you will find
232+ either traditional xUnit style packages relying on classes to define test suites
233+ and fixtures and test cases, or more recent testing packages (like
234+ [ Catch-2] ( https://github.com/catchorg/Catch2 ) for C++) that provide, through
235+ other means, ways to define setup and test cases than run independently. The
236+ common pattern is that setup code, that may be shared by multiple test cases, is
237+ usually re-evaluated for every test case so that, despite their potentially
238+ mutating interactions with the setup, test cases don't affect each other.
239+
240+ Some great articles and blog posts have explained how the leverage nested
241+ ` t.Run() ` calls to structure tests in way that is closer to BDD-style given /
242+ when / then paradigm. Unfortunately, when using thees approaches, and especially
243+ with shared setup sections, the test cases are no longer independent, as all
244+ branches are run sequentially, going up and down each branch and into the next
245+ branch, without resetting the setup.
246+
247+ The ` bdd ` package in ` go-testpredicate ` provides a way to write tests with a
248+ BDD-style structure, using the built-in ` testing.T ` , but evaluating the test
249+ cases in a bifurcated fashion, repeating the evaluation of each entire branch
250+ for every leaf test case, so that test cases are independent from each other
251+ again.
252+
253+ ### Usage overview
254+
255+ ` bdd.Wrap() ` or ` bdd.Given() ` are the root level function that setup and iterate
256+ through the bifurcated test evaluation context. They define blocks that receive
257+ a ` bdd.T ` instead of ` testing.T ` , but ` bdd.T ` is fully compatible with
258+ ` testing.T ` and can be used with any third party library that expect either the
259+ ` testing.TB ` interface or a subset of it (including out own ` verify.That() ` /
260+ ` require.That() ` ).
261+
262+ Nested and sibling bifurcated branches are defined with ` t.Run() ` (on ` bdd.T ` )
263+ or ` t.When() ` / ` t.Then() ` for BDD style.
264+
265+ > ** IMPORTANT:** In a bifurcated evaluation context, as defined by ` bdd.T ` , test
266+ > scenarios are run repeatedly in order to evaluate each branch (from root to
267+ > leaf) independently of each other. When a particular branch is being
268+ > evaluated, all the other forks and sub-branches are skipped; the other
269+ > branches are run in separated independent iterations of the scenario.
270+
271+ ### Usage, traditional style
272+
273+ ``` go
274+ package example_test
275+
276+ import (
277+ " testing"
278+ " github.com/maargenton/go-testpredicate/pkg/bdd"
279+ )
280+
281+ func TesTraditional (t *testing .T ) {
282+
283+ // Global immutable setup code can go here
284+
285+ bdd.Wrap (t, " Given something" , func (t *bdd.T ) {
286+
287+ // Local mutable setup code goes here
288+
289+ t.Run (" something happens" , func (t *bdd.T ) {
290+
291+ // When this code runs, the code in following `t.Run()` blocks
292+ // will be skipped.
293+ })
294+ t.Run (" something else happens" , func (t *bdd.T ) {
295+
296+ // When this code runs, all code in preceding `t.Run()` blocks
297+ // has been skipped and did not affect the local setup.
298+ })
299+ })
300+ }
301+ ```
302+
303+ ### Usage, BDD style
304+
305+ ``` go
306+ package bdd_test
307+
308+ import (
309+ " testing"
310+ " github.com/maargenton/go-testpredicate/pkg/bdd"
311+ )
312+
313+ func TestBDDStyle (t *testing .T ) {
314+
315+ // Global immutable setup code can go here
316+
317+ bdd.Given (t, " something" , func (t *bdd.T ) {
318+
319+ // Local mutable setup code goes here
320+
321+ t.When (" doing something" , func (t *bdd.T ) {
322+
323+ // or here
324+
325+ t.Then (" something happens" , func (t *bdd.T ) {
326+
327+ // When this code runs, the code in the following `t.Then()`
328+ // blocks will be skipped.
329+ })
330+ t.Then (" something else happens" , func (t *bdd.T ) {
331+
332+ // When this code runs, all code in preceding `t.Then()`
333+ // blocks has been skipped and did not affect the local setup.
334+ })
335+ })
336+ })
337+ }
338+ ```
0 commit comments