golden

package module
v0.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 1, 2024 License: MIT Imports: 10 Imported by: 0

README

Golden

Welcome to the Golden, a robust Go library designed to enhance your testing workflow by enabling easy management and comparison of golden files. A golden file is a reference file used to validate the output of a system against a known, correct result. It serves as a benchmark or "gold standard" to ensure that the system produces the expected output consistently. This library is perfect for developers looking to streamline their testing process, ensuring that their applications work as expected.

Features

  • Golden File Management: Easily manage your expected return values with golden files.
  • Comment Support in JSON: Unique functionality that allows you to add comments to your JSON files, enhancing readability and maintainability, despite JSON's native lack of comment support. Use the .jsonc extension for error-free IDE integration.
  • Precise JSON Comparison: Compare your expected JSON (from golden files) with the actual output of your code, with detailed reporting on discrepancies.
  • Flexible Configuration: Customize your testing with various options, including the ability to mark fields as skipped, whose values are non-deterministic.

Getting Started

To install Golden, simply run the following command:

go get github.com/tobbstr/golden

Usage

Using Golden is straightforward. Here's a simple example:

Let say there is a function GetPerson() person, and that there is a golden file in the testdata/get_person subfolder that contains the output of the GetPerson() in JSON-format.

The project layout:

.
├── testdata/
│   └── get_person/
│       └── happy_path.value.json
├── get_person.go
└── get_person_test.go

The GetPerson() function:

package domain

import (
    "fmt"
)

type person struct {
    Name string
    HairColour string
}

func GetPerson() person {
    // skipped for brevity
    return &person{Name: name, HairColour: hairColour}
}

Then verifying that the GetPerson() function works as expected is as easy as:

package domain

import (
    "fmt"
    "testing"

    "github.com/tobbstr/golden"
)

func TestGetPerson(t *testing.t) {
    // -------------------------------- Given --------------------------------
    want := "testdata/get_person/happy_path.value.json"

    // -------------------------------- When ---------------------------------
    got := GetPerson()

    // -------------------------------- Then ---------------------------------
    // Assert the return value
    golden.AssertJSON(t, want, got)
}

Features

Adding a field comment

When reviewing someone else's PR, and more specifically their golden files, it's not always easy to know what to check when reviewing. In these situations it would be helpful if it were possible to have comments on fields that are especially important to check. This library provides this functionality.

This adds a field comment.

// NOTE! The file extension is .jsonc, since standard JSON does not support comments.
want := "testdata/my_golden_file.jsonc"
golden.AssertJSON(t, want, got, golden.FieldComments(
    golden.FieldComment{Path:"data.users.0.age", Comment: "Should be 25"},
))

And this is what the resulting golden file looks like:

{
    "data": {
        "users": [
            {
                "name": "John",
                "age": 25 // Should be 25
            },
            {
                "name": "Eliana",
                "age": 32
            }
        ]
    }
}
Adding a file comment

For the same reasons as in the Adding a field comment section, it is beneficial to be able to add a comment at the top of the golden file.

This adds a file comment.

// NOTE! The file extension is .jsonc, since standard JSON does not support comments.
want := "testdata/my_golden_file.jsonc"
golden.AssertJSON(t, want, got, golden.FileComment("This is my file comment"))

And this is what the resulting golden file looks like:

/*
This is my file comment
*/

{
    "data": {
        "users": [
            {
                "name": "John",
                "age": 25
            },
            {
                "name": "Eliana",
                "age": 32
            }
        ]
    }
}
Skipping non-deterministic values

When the output of a function call contains non-deterministic values such as generated ids that are different every time you invoke it, then to make the golden files deterministic these fields' values must be made static. To achieve this, do the following.

// NOTE! The file extension is .jsonc, since standard JSON does not support comments.
want := "testdata/my_golden_file.jsonc"
golden.AssertJSON(t, want, got, golden.SkippedFields("data.users.0.age", "data.users.1.age"))

And this is what the resulting golden file looks like:

{
    "data": {
        "users": [
            {
                "name": "John",
                "age": "--* SKIPPED *--"
            },
            {
                "name": "Eliana",
                "age": "--* SKIPPED *--"
            }
        ]
    }
}

Contributing

Contributions are welcome! If you find any issues or have suggestions for improvements, please feel free to open an issue or submit a pull request.

License

Golden is licensed under the MIT License.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AssertJSON added in v0.2.0

func AssertJSON(t *testing.T, want string, got any, opts ...Option)

AssertJSON compares the expected JSON (want) with the actual value (got), and if they are different it marks the test as failed, but continues execution. The expected JSON is read from a golden file.

To update the golden file with the actual value instead of comparing with it, set the update flag to true.

func RequireJSON added in v0.2.0

func RequireJSON(t *testing.T, want string, got any, opts ...Option)

RequireJSON does the same as AssertJSON, but if the expected JSON (want) and the actual value (got) are different, it marks the test as failed and stops execution.

Types

type FieldComment added in v0.2.0

type FieldComment struct {
	// Path is the GJSON path to the field.
	// See https://github.com/tidwall/gjson/blob/master/SYNTAX.md
	//
	// Example: "data.user.name" for the following JSON:
	//
	//	{
	//	    "data": {
	//	        "user": {
	//	            "name": "John",
	//	        }
	//	    }
	//	}
	Path string
	// Comment is the comment that describes what to look for when inspecting the JSON field.
	Comment string
}

FieldComment is a comment that describes what to look for when inspecting the JSON field. The comment is added to the field specified by its Path.

type Option added in v0.2.0

type Option func(*testing.T, bool, *golden, any)

Option is a function that modifies the golden file. It is used to apply modifications to the golden file before comparing it with the actual result.

func FieldComments added in v0.2.0

func FieldComments(fieldComments ...FieldComment) Option

FieldComments adds comments to fields in the golden file. This is useful for making it easier for the reader to understand what to look for when inspecting the JSON field.

Example:
 {
   "age": 30, // This my field comment
 }

NOTE! Adding comments to JSON makes it invalid, since JSON does not support comments. To keep you IDE happy, i.e., for it not to show errors, make the file extension .jsonc. To do that, make sure the "want" file argument in the JSON() function call has the .jsonc extension.

func FileComment added in v0.2.0

func FileComment(comment string) Option

FileComment adds a comment to the top of the golden file. This is useful for providing context to the reader.

NOTE! Adding comments to JSON makes it invalid, since JSON does not support comments. To keep you IDE happy, i.e., for it not to show errors, make the file extension .jsonc. To do that, make sure the "want" file argument in the JSON() function call has the .jsonc extension.

func SkipFields added in v0.2.1

func SkipFields(fields ...string) Option

SkipFields replaces the value of the fields with "--* SKIPPED *--". The fields are specified by their GJSON path. See https://github.com/tidwall/gjson/blob/master/SYNTAX.md

The rules are as follows:

  • If the field is a nilable type and is nil, then it is not marked as skipped, since its "null" value is already deterministic.
  • If the field is a nilable type and has a non-nil value, then it is marked as skipped.
  • If the field is a non-nilable type, then it is marked as skipped.

Example: "data.user.Name" for the following JSON:

{
    "data": {
        "user": {
            "Name": "--* SKIPPED *--",
        }
    }
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL