Skip to content

feature: Add support for Testable Examples #506

@malte-brunst

Description

@malte-brunst

Did you check docs and existing issues?

Is your feature request related to a problem? Please describe.

When you define example functions (-> see 'Additional context' below if unfarmiliar with the feature) in your _test.go files, neotest-golang indicates that it runs them together with the normal test functions and indicates their status afterwards.
When you execute the whole file or start the watcher, neotest-golang indicates that a test example function passes, even though a wrong // Output: comment is provided.
Only when you run the example test function using the 'run nearest' or 'run all tests' functionality, does the function fail, as it is supposed to.

Example:

I implement a simple Add() function:

// Package add implements the Add function
package add

// Add takes two integers and returns their sum.
func Add(a, b int) int {
	return a + b
}

I create a add_test.go file that implements a normal test function and a testable example for Add() that declares a wrong Output: (0 instead of 8):

package add

import (
	"fmt"
	"testing"
)

func TestAdd(t *testing.T) {
	got := Add(2, 2)
	want := 4

	if got != want {
		t.Errorf("got %d, want %d", got, want)
	}
}

func ExampleAdd() {
	sum := Add(4, 4)
	fmt.Println(sum)
	// Output:
	// 0
}

I run go test on the add_test.go file which indicates a failure of the example:

❯ go test -json ./add/...
{"Time":"2025-11-20T17:02:23.279039581+01:00","Action":"start","Package":"github.com/malte-brunst/learn_go_with_tests/add"}
{"Time":"2025-11-20T17:02:23.284032736+01:00","Action":"run","Package":"github.com/malte-brunst/learn_go_with_tests/add","Test":"TestAdd"}
{"Time":"2025-11-20T17:02:23.284187964+01:00","Action":"output","Package":"github.com/malte-brunst/learn_go_with_tests/add","Test":"TestAdd","Output":"=== RUN   TestAdd\n"}
{"Time":"2025-11-20T17:02:23.284220462+01:00","Action":"output","Package":"github.com/malte-brunst/learn_go_with_tests/add","Test":"TestAdd","Output":"--- PASS: TestAdd (0.00s)\n"}
{"Time":"2025-11-20T17:02:23.284238372+01:00","Action":"pass","Package":"github.com/malte-brunst/learn_go_with_tests/add","Test":"TestAdd","Elapsed":0}
{"Time":"2025-11-20T17:02:23.284251224+01:00","Action":"run","Package":"github.com/malte-brunst/learn_go_with_tests/add","Test":"ExampleAdd"}
{"Time":"2025-11-20T17:02:23.284256282+01:00","Action":"output","Package":"github.com/malte-brunst/learn_go_with_tests/add","Test":"ExampleAdd","Output":"=== RUN   ExampleAdd\n"}
{"Time":"2025-11-20T17:02:23.284263695+01:00","Action":"output","Package":"github.com/malte-brunst/learn_go_with_tests/add","Test":"ExampleAdd","Output":"--- FAIL: ExampleAdd (0.00s)\n"}
{"Time":"2025-11-20T17:02:23.284269253+01:00","Action":"output","Package":"github.com/malte-brunst/learn_go_with_tests/add","Test":"ExampleAdd","Output":"got:\n"}
{"Time":"2025-11-20T17:02:23.284275244+01:00","Action":"output","Package":"github.com/malte-brunst/learn_go_with_tests/add","Test":"ExampleAdd","Output":"8\n"}
{"Time":"2025-11-20T17:02:23.284280281+01:00","Action":"output","Package":"github.com/malte-brunst/learn_go_with_tests/add","Test":"ExampleAdd","Output":"want:\n"}
{"Time":"2025-11-20T17:02:23.284288791+01:00","Action":"output","Package":"github.com/malte-brunst/learn_go_with_tests/add","Test":"ExampleAdd","Output":"0\n"}
{"Time":"2025-11-20T17:02:23.284293794+01:00","Action":"fail","Package":"github.com/malte-brunst/learn_go_with_tests/add","Test":"ExampleAdd","Elapsed":0}
{"Time":"2025-11-20T17:02:23.284298364+01:00","Action":"output","Package":"github.com/malte-brunst/learn_go_with_tests/add","Output":"FAIL\n"}
{"Time":"2025-11-20T17:02:23.285317156+01:00","Action":"output","Package":"github.com/malte-brunst/learn_go_with_tests/add","Output":"FAIL\tgithub.com/malte-brunst/learn_go_with_tests/add\t0.005s\n"}
{"Time":"2025-11-20T17:02:23.285356221+01:00","Action":"fail","Package":"github.com/malte-brunst/learn_go_with_tests/add","Elapsed":0.006}

I use neotest-golang instead and use the run test (leader + t + t in LazyVim) command.
A green tik indicates the functions succeeded.
The same wrong status is reported when you use the watcher mode (leader + t + w in LazyVim).
However, neotest-golang reports the correct status (FAIL), when I use the run all tests (leader + t + T in LazyVim) or the run nearest test command (leader + t + r).

Describe the solution you'd like to see.

I would like neotest-golang to report the correct test status (pass/fail) for example functions in the _test.go files.
The correct status should be reported no matter which execution mode (watcher, run whole file, run all tests, run nearest test) is chosen.
The correct status is the one provided when using the standard go test command.

Describe alternatives you've considered.

There are no alternatives.

Additional context

As with typical tests, examples are functions that reside in a package’s _test.go files. Unlike normal test functions, though, example functions take no arguments and begin with the word Example instead of Test.
Example from the Go example repository:

package reverse_test

import (
    "fmt"

    "golang.org/x/example/hello/reverse"
)

func ExampleString() {
    fmt.Println(reverse.String("hello"))
    // Output: olleh
}

Running the package’s test suite, we can see the example function is executed with no further arrangement from us:

$ go test -v
=== RUN   TestString
--- PASS: TestString (0.00s)
=== RUN   ExampleString
--- PASS: ExampleString (0.00s)
PASS
ok      golang.org/x/example/hello/reverse  0.209s

As it executes the example, the testing framework captures data written to standard output and then compares the output against the example’s “Output:” comment. The test passes if the test’s output matches its output comment or failes otherwise.

Godoc uses a naming convention to associate an example function with a package-level identifier.

func ExampleFoo()     // documents the Foo function or type
func ExampleBar_Qux() // documents the Qux method of type Bar
func Example()        // documents the package as a whole

Following this convention, godoc displays the ExampleString example alongside the documentation for the String function.

Multiple examples can be provided for a given identifier by using a suffix beginning with an underscore followed by a lowercase letter. Each of these examples documents the String function:

func ExampleString()
func ExampleString_second()
func ExampleString_third()

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions