Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@ be.NilErr(t, err) // bad
be.Nonzero(t, err) // good
```

Check substring containment:
Check for regexp matching:

```go
be.In(t, "world", "hello, world") // good
be.In(t, "World", "hello, world") // bad
// t.Fatal("World" not in "hello, world")
be.NotIn(t, "\x01", []byte("\a\b\x00\r\t")) // good
be.NotIn(t, "\x00", []byte("\a\b\x00\r\t")) // bad
// t.Fatal("\x00" in "\a\b\x00\r\t")
be.Match(t, `world`, "hello, world") // good
be.Match(t, `World`, "hello, world") // bad
// t.Fatal(`/World/ !~ "hello, world"`)
be.Match(t, `^\W*$`, []byte("\a\b\x00\r\t")) // good
be.Match(t, `^\W*$`, []byte("\a\bo\r\t")) // bad
// t.Fatal(`/^\W*$/ !~ "\a\bo\r\t"`)
```

Check how long something rangeable is:
Expand All @@ -80,7 +80,7 @@ Test anything else:

```go
be.True(t, o.IsValid())
be.True(t, len(pages) >= 20)
be.False(t, s.CanParse())
```

Test using goldenfiles:
Expand Down Expand Up @@ -108,7 +108,7 @@ testfile.Run(t, "testdata/*.txt", func(t *testing.T, path string) {
## Philosophy
Tests usually should not fail. When they do fail, the failure should be repeatable. Therefore, it doesn't make sense to spend a lot of time writing good test messages. (This is unlike error messages, which should happen fairly often, and in production, irrepeatably.) Package be is designed to simply fail a test quickly and quietly if a condition is not met with a reference to the line number of the failing test. If the reason for having the test is not immediately clear from context, you can write a comment, just like in normal code. If you do need more extensive reporting to figure out why a test is failing, use `be.DebugLog` or `be.Debug` to capture more information.

Most tests just need simple equality testing, which is handled by `be.Equal` (for comparable types), `be.AllEqual` (for slices of comparable types), and `be.DeepEqual` (which relies on `reflect.DeepEqual`). Another common test is that a string or byte slice should contain or not some substring, which is handled by `be.In` and `be.NotIn`. Rather than package be providing every possible test helper, you are encouraged to write your own advanced helpers for use with `be.True`, while package be takes away the drudgery of writing yet another simple `func nilErr(t *testing.T, err) { ... }`.
Most tests just need simple equality testing, which is handled by `be.Equal` (for comparable types), `be.AllEqual` (for slices of comparable types), and `be.DeepEqual` (which relies on `reflect.DeepEqual`). Another common test is that a string or byte slice should match some pattern, which is handled by `be.Match`. Rather than package be providing every possible test helper, you are encouraged to write your own advanced helpers for use with `be.True`, while package be takes away the drudgery of writing yet another simple `func nilErr(t *testing.T, err) { ... }`.

The testfile subpackage has functions that make it easy to write file-based tests that ensure that the output of some transformation matches a [golden file](https://softwareengineering.stackexchange.com/questions/358786/what-are-golden-files). Subtests can automatically be run for all files matching a glob pattern, such as `testfile.Run(t, "testdata/*/input.txt", ...)`. If the test fails, the failure output will be written to a file, such as "testdata/basic-test/-failed-output.txt", and then the output can be examined via diff testing with standard tools. Set the environmental variable `TESTFILE_UPDATE` to update the golden file.

Expand Down
12 changes: 6 additions & 6 deletions be_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ func Example() {

type mytype string
var mystring mytype = "hello, world"
be.In(t, "world", mystring) // good
be.In(t, "World", mystring) // bad
be.NotIn(t, "\x01", []byte("\a\b\x00\r\t")) // good
be.NotIn(t, "\x00", []byte("\a\b\x00\r\t")) // bad
be.Match(t, `world`, mystring) // good
be.Match(t, `World`, mystring) // bad
be.Match(t, `^\W*$`, []byte("\a\b\x00\r\t")) // good
be.Match(t, `^\W*$`, []byte("\a\bo\r\t")) // bad

seq := strings.FieldsSeq("1 2 3 4")
be.EqualLength(t, 4, seq) // good
Expand All @@ -55,8 +55,8 @@ func Example() {
// got: <nil>
// got errors.Is(<nil>, permission denied) == false
// got errors.As((O_o), **fs.PathError) == false
// "World" not in "hello, world"
// "\x00" in "\a\b\x00\r\t"
// /World/ !~ "hello, world"
// /^\W*$/ !~ "\a\bo\r\t"
// want len(seq) == 1; got at least 2
// want len(seq) >= 5; got 4
// want len(seq) >= 4; got 3
Expand Down
10 changes: 10 additions & 0 deletions be_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ func Test(t *testing.T) {
seq2 := maps.All(map[int]int{1: 1})
be.EqualLength(tb, 1, seq2)
},
func(tb testing.TB) {
be.In(tb, "world", "Hello, world!")
be.NotIn(tb, "\x01", []byte("\a\b\x00\r\t"))
},
}

for _, test := range okayTests {
Expand Down Expand Up @@ -88,6 +92,12 @@ func Test(t *testing.T) {
close(ch)
be.EqualLength(tb, 1, ch)
},
func(tb testing.TB) {
be.In(tb, "World", "Hello, world!")
},
func(tb testing.TB) {
be.NotIn(tb, "\x00", []byte("\a\b\x00\r\t"))
},
}

for _, test := range badTests {
Expand Down
14 changes: 0 additions & 14 deletions len_test.go

This file was deleted.

28 changes: 28 additions & 0 deletions match.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package be

import (
"reflect"
"regexp"
"testing"
)

// Match calls t.Fatalf if got does not match the [regexp] pattern.
//
// The pattern must compile.
func Match[byteseq ~string | ~[]byte](t testing.TB, pattern string, got byteseq) {
t.Helper()
reg := regexp.MustCompile(pattern)
if !match(reg, got) {
t.Fatalf("/%s/ !~ %q", pattern, got)
}
}

func match[byteseq ~string | ~[]byte](reg *regexp.Regexp, got byteseq) bool {
switch rv := reflect.ValueOf(got); rv.Kind() {
case reflect.String:
return reg.MatchString(rv.String())
case reflect.Slice:
return reg.Match(rv.Bytes())
}
panic("unreachable")
}
25 changes: 25 additions & 0 deletions panic_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package be_test

import (
"testing"

"github.com/carlmjohnson/be"
)

func TestLen(t *testing.T) {
// Make sure integers aren't treated as rangeable
be.Nonzero(t, be.Panicked(func() {
be.EqualLength(t, 0, 0)
}))
}

func TestMatch(t *testing.T) {
// Make sure bad regexp patterns panic
pval := be.Panicked(func() {
be.Match(t, `\`, "")
})
be.Nonzero(t, pval)
s, ok := pval.(string)
be.True(t, ok)
be.Match(t, `^regexp: Compile\(`, s)
}