Skip to content

Commit

Permalink
Big refresh (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
FollowTheProcess authored Jan 6, 2025
1 parent 1802090 commit 41b13e6
Show file tree
Hide file tree
Showing 48 changed files with 1,035 additions and 689 deletions.
68 changes: 35 additions & 33 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ on:
push:
branches:
- main
tags:
- v*

concurrency:
group: ${{ github.ref }}
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true

permissions: read-all
permissions: {}

jobs:
test:
name: Test
runs-on: ${{ matrix.os }}
permissions:
contents: read
strategy:
matrix:
os:
Expand All @@ -36,10 +36,14 @@ jobs:

- name: Run Tests
run: go test -race ./...
env:
NO_COLOR: true

cov:
name: CodeCov
runs-on: ubuntu-latest
permissions:
contents: read

steps:
- name: Checkout Code
Expand All @@ -52,16 +56,19 @@ jobs:

- name: Run Tests
run: go test -race -cover -covermode=atomic -coverprofile=./coverage.out ./...
env:
NO_COLOR: true

- name: Coverage
uses: codecov/codecov-action@v5
with:
files: ./coverage.out
token: ${{ secrets.CODECOV_TOKEN }}

lint:
name: Lint
runs-on: ubuntu-latest
permissions:
contents: read

steps:
- name: Checkout Code
Expand All @@ -72,46 +79,41 @@ jobs:
with:
go-version-file: go.mod

- name: Clean Mod Cache # See https://github.com/golangci/golangci-lint-action/issues/135
run: go clean -modcache

- name: Run Linting
uses: golangci/golangci-lint-action@v6
with:
version: latest

release:
name: Release
vulncheck:
name: Vulncheck
runs-on: ubuntu-latest
permissions:
contents: write

needs:
- test
- cov
- lint

if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
contents: read

steps:
- name: Checkout Code
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
fetch-depth: 0
go-version-file: go.mod

- name: Install govulncheck
run: go install golang.org/x/vuln/cmd/govulncheck@latest

- name: Fetch Existing Tags
run: git fetch --force --tags
- name: Run govulncheck
run: govulncheck ./...

- name: Parse Release Version
id: version
run: |
VERSION=${GITHUB_REF#refs/tags/v}
echo "version=$VERSION" >> $GITHUB_OUTPUT
typos:
name: Typos
runs-on: ubuntu-latest
permissions:
contents: read

- name: Publish Draft Release
uses: release-drafter/release-drafter@v6
with:
version: ${{ steps.version.outputs.version }}
publish: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Checkout Code
uses: actions/checkout@v4

- name: Check for Typos
uses: crate-ci/[email protected]
39 changes: 39 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Release

on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'

permissions: {}

jobs:
release:
name: Release
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: read

steps:
- name: Checkout Code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Fetch Existing Tags
run: git fetch --force --tags

- name: Parse Release Version
id: version
run: |
VERSION=${GITHUB_REF#refs/tags/v}
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Publish Draft Release
uses: release-drafter/release-drafter@v6
with:
version: ${{ steps.version.outputs.version }}
publish: true
env:
GITHUB_TOKEN: ${{ github.token }}
7 changes: 7 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ linters:
- unused
- whitespace

issues:
exclude-rules:
- path: test_test.go
linters:
- thelper # The entire package is effectively a t.Helper
- goconst # Lots of repetition in here

linters-settings:
errcheck:
check-type-assertions: true
Expand Down
96 changes: 19 additions & 77 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,46 +42,38 @@ func TestSomething(t *testing.T) {

test.True(t, true) // Passes
test.False(t, true) // Fails

// Get $CWD/testdata easily
test.Data(t) // /Users/you/project/package/testdata

// Check against contents of a file including line ending normalisation
file := filepath.Join(test.Data(t), "expected.txt")
test.File(t, "hello\n", file)

// Just like the good old reflect.DeepEqual, but with a nicer format
test.DeepEqual(t, []string{"hello"}, []string{"world"}) // Fails
}
```

### Self Documenting Tests

> [!TIP]
> Line comments on the line you call most `test` functions on will be shown in failure messages as additional context
### Add Additional Context

That means you can have additional context in the failure message, as well as helpful comments explaining the assertion to readers of your code
`test` provides a number of options to decorate your test log with useful context:

```go
func TestSomething(t *testing.T) {
test.Equal(t, "apples", "oranges") // Fruits are not equal
func TestDetail(t *testing.T) {
test.Equal(t, "apples", "oranges", test.Title("Fruit scramble!"), test.Context("Apples are not oranges!"))
}
```

Will get you a failure message like:
Will get you an error log in the test that looks like this...

```shell
--- FAIL: TestSomething (0.00s)
something_test.go:1:
Not Equal // Fruits are not equal
---------
```plaintext
--- FAIL: TestDemo (0.00s)
test_test.go:501:
Fruit scramble!
---------------
Got: apples
Wanted: oranges
(Apples are not oranges!)
FAIL
```

### Non Comparable Types

`test` uses Go 1.18+ generics under the hood for most of the comparison, which is great, but what if your types don't satisfy `comparable`. We also provide
`test` uses generics under the hood for most of the comparison, which is great, but what if your types don't satisfy `comparable`. We also provide
`test.EqualFunc` and `test.NotEqualFunc` for those exact situations!

These allow you to pass in a custom comparator function for your type, if your comparator function returns true, the types are considered equal.
Expand All @@ -98,42 +90,15 @@ func TestNonComparableTypes(t *testing.T) {

test.EqualFunc(t, a, b, sliceEqual) // Passes

// Can also use e.g. the new slices package
test.EqualFunc(t, a, b, slices.Equal[string]) // Also passes :)
// Can also use any function here
test.EqualFunc(t, a, b, slices.Equal) // Also passes :)

test.EqualFunc(t, a, c, slices.Equal[string]) // Fails
test.EqualFunc(t, a, c, slices.Equal) // Fails
}
```

You can also use this same pattern for custom user defined types, structs etc.

### Rich Comparison

Large structs or long slices can often be difficult to compare using `reflect.DeepEqual`, you have to scan for the difference yourself. `test` provides a
`test.Diff` function that produces a rich text diff for you on failure:

```go
func TestDiff(t *testing.T) {
// Pretend these are very long, or are large structs
a := []string{"hello", "world"}
b := []string{"hello", "there"}

test.Diff(t, a, b)
}
```

Will give you:

```plain
--- FAIL: TestDiff (0.00s)
main_test.go:14: Mismatch (-want, +got):
[]string{
"hello",
- "there",
+ "world",
}
```

### Table Driven Tests

Table driven tests are great! But when you test errors too it can get a bit awkward, you have to do the `if (err != nil) != tt.wantErr` thing and I personally
Expand Down Expand Up @@ -232,29 +197,6 @@ func TestOutput(t *testing.T) {

Under the hood `CaptureOutput` temporarily captures both streams, copies the data to a buffer and returns the output back to you, before cleaning everything back up again.

### Golden Files

`test` has great support for golden files:

```go
func TestFile(t *testing.T) {
got := "some contents\n"
want := filepath.Join(test.Data(t), "golden.txt")

test.File(t, got, want)
}
```

This will read the file, normalise line endings and then generate an output almost like a git diff:

```patch
--- want
+++ got
@@ -1 +1 @@
-some file contents
+some contents
```

### Credits

This package was created with [copier] and the [FollowTheProcess/go_copier] project template.
Expand Down
Loading

0 comments on commit 41b13e6

Please sign in to comment.