Skip to content

Golden files

A golden-file test renders part of your UI and compares the result with a file saved earlier, called the master. If anything changes, the test fails and saves what it rendered, so you can see what changed. The test package can compare two kinds of output:

  • Markup: a text description of the rendered object tree, with each object’s type, position, size and text. It is easy to read in a diff.
  • Images: the rendered pixels, drawn by Bellui’s software renderer, so no GPU is needed.

A UI to test

func statusCard(title, detail string) core.CanvasObject {
	return container.NewVBox(
		widget.NewLabelWithStyle(title, bellui.TextAlignLeading, bellui.TextStyle{Bold: true}),
		widget.NewLabel(detail),
	)
}

Comparing markup

func TestStatusCard_Markup(t *testing.T) {
	test.NewTempApp(t)

	card := statusCard("Backup", "Last run: today")
	test.AssertObjectRendersToMarkup(t, "status_card.xml", card)
}

test.AssertObjectRendersToMarkup renders the object at its minimum size and compares the markup with testdata/status_card.xml, relative to the test’s package folder. The master for this example looks like this:

<canvas size="107x68">
	<content>
		<container size="107x68">
			<widget size="107x32" type="*widget.Label">
				<widget size="107x32" type="*widget.RichText">
					<text bold pos="8,8" size="46x16">Backup</text>
				</widget>
			</widget>
			<widget pos="0,36" size="107x32" type="*widget.Label">
				<widget size="107x32" type="*widget.RichText">
					<text pos="8,8" size="91x16">Last run: today</text>
				</widget>
			</widget>
		</container>
	</content>
</canvas>

Comparing images

func TestStatusCard_Image(t *testing.T) {
	test.NewTempApp(t)

	card := statusCard("Backup", "Last run: today")
	test.AssertObjectRendersToImage(t, "status_card.png", card)
}

test.AssertObjectRendersToImage does the same with a PNG master. The comparison allows small differences, so minor anti-aliasing changes between machines do not fail the test.

Creating and updating masters

The first time a test runs there is no master. The test fails, and writes what it rendered to testdata/failed/. To create or refresh the masters instead, run the tests with UPDATE_TESTDATA set:

UPDATE_TESTDATA=1 go test ./...

Each master is written to testdata/ and the test passes. Look at the new files before you commit them: a master is only useful if what it shows is right.

When a comparison fails later, the actual output is written to testdata/failed/ under the same name, and the failure message gives its path. Compare it with the master, then either fix the code or rerun with UPDATE_TESTDATA=1 to accept the change. Add testdata/failed/ to your .gitignore.

Rendering whole windows

The …Object… functions size a fresh canvas to the object. To check a whole window, including anything drawn outside one object’s bounds, render its canvas with test.AssertRendersToMarkup or test.AssertRendersToImage, passing w.Canvas() of a test window.

A canvas created with test.NewCanvas has no renderer, so its images come out blank. The AssertObjectRendersToImage helper sets up a software renderer for you; for your own canvas use test.NewCanvasWithPainter.

When the tests run with the mobile build tag, the helpers first look for a master under testdata/mobile/ and fall back to the shared one. This lets widgets that look different on touch screens keep separate masters.