• Stars
    star
    282
  • Rank 146,549 (Top 3 %)
  • Language
    Go
  • License
    Other
  • Created about 4 years ago
  • Updated 7 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Automatically update your Go tests

autogold - automatically update your Go tests Hexops logo

Go Reference

Go CI codecov Go Report Card

autogold makes go test -update automatically update your Go tests (golden files and Go values in e.g. foo_test.go).

~5m introduction available on YouTube:

"It's 2021: you shouldn't have to update Go tests manually"

Installation

go get -u github.com/hexops/autogold/v2

Automatic golden files

Write in a Go test:

import "github.com/hexops/autogold/v2"
...
autogold.ExpectFile(t, got)

go test -update will now create/update a testdata/<test name>.golden file for you automatically. If your tests change over time you can use go test -update -clean to also have it remove unused golden files.

Automatic inline test updating

In a Go test, simply call autogold.Expect(want).Equal(t, got), passing nil as the value you want initially:

func TestFoo(t *testing.T) {
	...
	autogold.Expect(nil).Equal(t, got)
}

Run go test -update and autogold will automatically update the autogold.Expect(want) Go syntax with the actual value your test got. It works with complex Go structs, slices, strings, etc.

Diffs

Anytime your test produces a result that is unexpected, you'll get a nice diff showing exactly what changed. It does this by converting values at runtime directly to a formatted Go AST, and using the same diffing library the Go language server uses:

--- FAIL: TestFoo (0.08s)
    autogold.go:91: mismatch (-want +got):
        --- want
        +++ got
        @@ -1 +1 @@
        +&example.Baz{Name: "Jane", Age: 31}

Subtesting

Table-driven Go subtests are supported nicely as you can call .Equal(got) later, so that go test -update will update your table-driven test values defined earlier for you:

func TestTime(t *testing.T) {
	testCases := []struct {
		gmt    string
		loc    string
		expect autogold.Value // autogold: the value we expect
	}{
		{"12:31", "Europe/Zuri", autogold.Expect(nil)},
		{"12:31", "America/New_York", autogold.Expect(nil)},
		{"08:08", "Australia/Sydney", autogold.Expect(nil)},
	}
	for _, tc := range testCases {
		t.Run(tc.loc, func(t *testing.T) {
			loc, err := time.LoadLocation(tc.loc)
			if err != nil {
				t.Fatal("could not load location")
			}
			gmt, _ := time.Parse("15:04", tc.gmt)
			got := gmt.In(loc).Format("15:04")

			tc.expect.Equal(t, got) // autogold: tell it the value our test produced
		})
	}
}

It works by finding the relevant autogold.Expect(want) call for you based on callstack information / matching line number in the file, and then rewrites the nil parameter (or any other value that was there.)

What are golden files, when should they be used?

Golden files are used by the Go authors for testing the standard library, the gofmt tool, etc. and are a common pattern in the Go community for snapshot testing. See also "Testing with golden files in Go" - Chris Reeves

Golden files make the most sense when you'd otherwise have to write a complex multi-line string or large Go structure inline in your test, making it hard to read.

In most cases, you should prefer inline snapshots, subtest golden values, or traditional Go tests.

Command line syntax: put -update at the end

-update should go at the end of your go test command, otherwise for some reason stdout will be considered a terminal and color will be turned on for libraries like fatih/color. Example:

go test -count=1 -run TestSomething . -update

Custom formatting

valast is used to produce Go syntax at runtime for the Go value you provide. If the default output is not to your liking, you have options:

  • Pass a string to autogold: It will be formatted as a Go string for you in the resulting .golden file / in Go tests.
  • Use your own formatting (JSON, etc.): Make your got value of type autogold.Raw("foobar"), and it will be used as-is for .golden files (not allowed with inline tests.)
  • Exclude unexported fields: autogold.ExpectFile(t, got, autogold.ExportedOnly())

Backwards compatibility

  • As is the case with gofmt, different Go versions may produce different formattings (although rare.)
  • Minor versions of autogold (e.g. v1.0, v1.1) may alter the formatting of .golden files, although we will be mindful of such changes.
  • Major versions of autogold (e.g. v1, v2) will be used for any major changes in output that would be difficult to review (we expect this will be rare in practice.)

Alternatives comparison

The following are alternatives to autogold, making note of the differences we found that let us to create autogold:

Changelog

v2.2.1

Updated to valast v1.4.4:

  • valast.Addr is replaced by valast.Ptr, which uses Go generics and looks cleaner.
  • time.Time values are now supported.

v2.2.0

  • If autogold is used in packages with an -update flag already defined, now no conflict occurs. This enables autogold to be used with other 'golden' packages without conflict.
  • Fixed an issue where _test packages using types from non-test packages would sometimes result in the wrong package name qualifier.

v2.1.0

Added support for building in Bazel / working around a bug in Bazel / Go's packages.Load functionality. This feature can be enabled using ENABLE_BAZEL_PACKAGES_LOAD_HACK=true. For more details see #40 and golang/go#57304

v2.0.3

Fixed an issue where updating inline tests could cause a deadlock.

v2.0.2

Writing a unique name with inline tests is no longer required. Previously you must write a unique name as the first parameter to Want and it must have been inside a TestFoo function for autogold to find it:

func TestFoo(t *testing.T) {
	...
	autogold.Want("unique name inside TestFoo", want).Equal(t, nil)
}

This can be rewritten as autogold.Expect(want).Equal(t, got) and no unique name is required, the function call can be placed anywhere inside your Go test file as autogold will now update the invocation based on callstack information:

func TestFoo(t *testing.T) {
	...
	autogold.Expect(want).Equal(t, nil)
}

Additionally, CLI flag behavior has been improved substantially based on experience working in very large enterprise Go codebases:

  • -update now behaves like -update-only, it no longer removes unused golden files which is faster in very large codebases. Instead, you may use -update -clean to remove unused golden files. -update-only is removed.
  • Previously autogold would fail tests when running -update, meaning you may need to run go test -update many times to get to your desired end-state if updating a lot of test cases. Now we match the behavior of OCaml expect tests in not failing tests by default (you can now specify -fail-on-update)
  • -fail-on-update now uses t.FailNow() instead of t.Fatal() to allow as many tests as possible to succeed when performing an update operation.
  • autogold.Want has been deprecated in favor of autogold.Expect
  • Fixed invalid cross-device link errors on some systems.

Finally, please note the renaming of functions before and after:

  • Inline tests: autogold.Want -> autogold.Expect
  • File tests: autogold.Equal -> autogold.ExpectFile
Automating the migration with Comby

You can automatically migrate from v1 to v2 using the following Comby configuration:

autogold.comby.toml
# autogold.comby.toml
[update-imports]

match="\"github.com/hexops/autogold\""
rewrite="\"github.com/hexops/autogold/v2\""

[update-api-want]

match="autogold.Want(:[desc], :[v])"
rewrite="autogold.Expect(:[v])"

[update-api-equal]

match="autogold.Equal(:[v])"
rewrite="autogold.ExpectFile(:[v])"

Assuming Comby is available on your system, you can run the following command to apply the changes:

$ go get -u github.com/hexops/autogold/v2
$ comby -config autogold.comby.toml -matcher .go -exclude-dir vendor,node_modules -in-place
$ go mod tidy
$ git diff # show the changes applied by comby

v1.3.1

  • Improved Go code formatting (updated valast and gofumpt versions)
  • Added usage of t.Helper to improve line:column information of test failures.
  • Fixed an issue where -update subtest names could collide and incorrectly fail tests.
  • Fixed a data race when -update is used
  • Diffs are now printed with ANSII color codes

More Repositories

1

dockerfile

Dockerfile best-practices for writing production-worthy Docker images.
Dockerfile
4,033
star
2

mach

zig game engine & graphics toolkit
Zig
3,065
star
3

vecty

Vecty lets you build responsive and dynamic web frontends in Go using WebAssembly, competing with modern web frameworks like React & VueJS.
Go
2,790
star
4

mach-glfw

Ziggified GLFW bindings with 100% API coverage, zero-fuss installation, cross compilation, and more.
Zig
344
star
5

valast

Convert Go values to their AST
Go
305
star
6

fastfilter

fastfilter: Binary fuse & xor filters for Zig (faster and smaller than bloom filters)
Zig
249
star
7

mach-core

window+input+GPU, truly cross-platform
Zig
184
star
8

mach-gpu

mach/gpu: truly cross-platform WebGPU graphics for Zig
Zig
182
star
9

mach-gpu-dawn

Google's Dawn WebGPU implementation, cross-compiled with Zig into a single static library
Zig
144
star
10

gotextdiff

Unified text diffing in Go (copy of the internal diffing packages the officlal Go language server uses)
Go
129
star
11

mach-sysgpu

Highly experimental, blazingly fast, lean & mean descendant of WebGPU written in Zig
Zig
95
star
12

mach-examples

Mach engine examples
Zig
86
star
13

dawn

Fork of dawn.googlesource.com/dawn with generated code and third-party dependencies committed
C++
78
star
14

zorex

Zorex: the omnipotent regex engine
Zig
66
star
15

mach-dxcompiler

DXC built using Zig
C++
53
star
16

Azure-Kinect-Python

Python 3 bindings for the Azure Kinect SDK
Python
48
star
17

mach-freetype

Ziggified Freetype 2 bindings with zero-fuss installation, cross compilation, and more.
Zig
46
star
18

zgo

Go + Zig = πŸ’• | Zig and Go are best friends
Go
38
star
19

mach-glfw-vulkan-example

mach-glfw Vulkan example
Zig
37
star
20

mach-glfw-opengl-example

Example for using mach-glfw with zig-opengl
Zig
35
star
21

mach-ecs

Entity Component System from first-principles designed for Zig
34
star
22

mach-sysaudio

cross-platform low-level audio IO in Zig
Zig
29
star
23

mach-sysjs

enables Zig/WASM to speak to JavaScript
Zig
28
star
24

awesome-zig-gamedev

Comprehensive collection of maintained zig gamedev projects & libraries
17
star
25

xcode-frameworks

XCode frameworks packaged for the Zig build system
C
17
star
26

mach-core-starter-project

The result of running through https://machengine.org/core/getting-started
Zig
16
star
27

mach-gamemode

mach-gamemode: make your Linux games go brrr
14
star
28

harfbuzz

A fork of harfbuzz packaged for the Zig build system
C++
13
star
29

brotli

A fork of brotli packaged for the Zig build system
C
12
star
30

cmder

Lightweight Go pattern for writing CLIs with subcommands
Go
12
star
31

pgtrgm_emperical_measurements

Emperical measurements of pg_trgm performance at scale
Shell
11
star
32

zigmonthly.org

Curated Zig news brought to you by @slimsag each month
SCSS
11
star
33

mach-system-sdk

DirectX 12 headers for MinGW/Zig, cross-compile DX12/Metal/etc with Zig, etc.
10
star
34

glfw

A fork of GLFW packaged for the Zig build system
C
10
star
35

machengine.org

machengine.org website, documentation, etc.
SCSS
10
star
36

sdk-macos-11.3

Also see https://github.com/hexops/sdk-macos-12.0
C
9
star
37

libmach

C API to Mach core and Mach engine
C
9
star
38

vulkan-zig-generated

Snektron/vulkan-zig, but generated and comitted for use as a package manager dependency
Zig
9
star
39

wrench

[bot] 🐡 Wrench here! Let's fix this! πŸ”§
Go
9
star
40

freetype

A fork of freetype packaged for the Zig build system
C
9
star
41

mach-editor

Mach CLI and graphical editor
Zig
8
star
42

devlog

Hexops devlog
SCSS
7
star
43

vulkan-headers

A fork of vulkan-headers packaged for the Zig build system
C++
7
star
44

x11-headers

x11-headers packaged for the Zig build system
C
7
star
45

mach-model3d

compact, featureful model format & alternative to glTF
C
7
star
46

font-assets

Various fonts packaged for the Zig build system
Zig
7
star
47

sinter

Sinter text search engine
Zig
6
star
48

mach-objc

Generated Objective-C bindings for Zig
Zig
6
star
49

basisu

A fork of basisu packaged for the Zig build system
C
6
star
50

sdk-macos-12.0

macOS 12.0 Monterey SDK for Mach engine
C
6
star
51

mach-basisu

basis universal (supercompressed textures) for Zig
Zig
6
star
52

mach-opus

Opus audio decoding and encoding for Zig via the battle-hardened xiph.org libopus
Zig
5
star
53

linux-audio-headers

linux-audio-headers packaged for the Zig build system
C
5
star
54

mach-rs

Rust bindings for Mach
5
star
55

wayland-headers

wayland-headers packaged for the Zig build system
C
5
star
56

spirv-tools

A fork of spirv-tools packaged for the Zig build system
C++
4
star
57

ControlCursor

A C# library for getting and setting global cursor position.
C#
4
star
58

direct3d-headers

Enables using the latest Direct3D headers and libraries with Zig
C
4
star
59

mach-flac

FLAC audio decoding and encoding for Zig via the battle-hardened xiph.org libflac
Zig
4
star
60

hexi-old

Hexi: Game Engine & Graphics Toolkit
Rust
4
star
61

sycl24

Software You Can Love 2024 workshop
Zig
4
star
62

spirv-cross

A fork of spirv-cross packaged for the Zig build system
C++
3
star
63

hexops.com

hexops.com website
SCSS
3
star
64

opengl-headers

opengl-headers packaged for the Zig build system
C
3
star
65

soundio

libsoundio upstream sources used by Mach engine
C
3
star
66

sdk-macos-13.3

macOS 13.3 Ventura SDK for Mach engine
C
3
star
67

sdk-linux-x86_64

Linux x86_64 SDK for Mach engine
C++
3
star
68

mach-ggml

Zig
3
star
69

lordofzero

Shell
3
star
70

opusfile

A fork of opusfile packaged for the Zig build system
C
2
star
71

sdk-linux-aarch64

Linux aarch64 SDK for Mach engine
C++
2
star
72

ogg

A fork of ogg packaged for the Zig build system
C
2
star
73

directx-headers

A fork of DirectX-Headers, packaged with the Zig build system and compatible with cross-compilation
C
2
star
74

sdk-windows-x86_64

Windows x86_64 SDK for Mach engine
C
2
star
75

mach-objc-generator

Zig Objective-C binding generator
Zig
2
star
76

mach-example-assets

mach-core examples assets
Zig
2
star
77

heapbit

1
star
78

tridex-assets

Static contents / assets for Tridex
1
star
79

opus

A fork of opus packaged for the Zig build system
C
1
star
80

opusenc

A fork of libopusenc packaged for the Zig build system
C
1
star
81

ztemplate

Hexops Zig template repository
Zig
1
star
82

flac

A fork of flac packaged for the Zig build system
C
1
star
83

stackptr

1
star