Env

gopls version: master (5397e65c)

Proposal

GoLand offers a Go to test shortcut (Ctrl+Shift+T) that allows users to jump to test definitions (if any) or create a new test:

I propose to add a similar feature to gopls. Initially, I was going to suggest to add a new command Go to TestXxx to the Source Action window (Alt+. in VSCode), mimicking GoLand. However, I think implementing it as a CodeLens would be more convenient: users can immediately see what functions have tests.

An MVP implementation can be found at https://go.dev/cl/696395. Examples of Go to TestXxx CodeLens for slices package (actually, Go to {Test|Example|Benchmark|Fuzz}Xxx):

Questions

  1. I am not sure what algorithm should be used to match functions with their tests. A naive approach looks like this:

  2. Try to find test functions that match name exactly:

    • TestFoo for Foo
    • Test_foo for foo
    • TestBuffer_Bytes for (Buffer).Bytes
  3. Search with the capitalized first letter:

    • TestFoo for foo
    • TestBuffer_Bytes for (buffer).Bytes

    This algorithm should not have many false positives, but it fails to match TestReplaceGrow, TestReplacePanics, etc. with slices.Replace. We could use a prefix search like GoLand does, but I am not sure how to exclude TestDeleteFunc from matches for Delete:

  1. Maybe it's worth implementing this feature both as CodeLens and Source Action?
Pros Cons
CodeLens - obvious which functions have tests
- requires only a single click
- requires a mouse click [0]
- noisy when a function has many tests [1]
Source Action - easy to display all tests - not obvious whether a function has tests
- requires 3 actions: Alt+., move selection, Enter
  • [0] Actually, it's possible to trigger CodeLens via keyboard, but there are some limitations - see microsoft/vscode#91232
  • [1] Naive algorithm doesn't not produce that many CodeLenses

We could display only one test function (per type) as a CodeLens (with title like Go to TestXxx (has X more tests)) and list all the test functions in the Source Actions menu. This approach should address all the mentioned issues.

Comment From: gabyhelp

Related Issues

(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)

Comment From: IlyasYOY

Hi! I think this is a fantastic idea and I’m really excited to see it land.

One suggestion I have is to avoid getting bogged down in finding a “perfect” algorithm for locating the corresponding tests right away. For an initial implementation it’s perfectly fine to rely on the existing naming conventions, especially for examples.

The Go blog defines the naming rules for Example functions here: https://go.dev/blog/examples#example-function-names

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

You can start by matching these patterns and, if possible, reuse any code that already handles them. Once the basic behavior is in place, the matching logic can be refined later.

Thanks for the great proposal – looking forward to seeing it in action!

Comment From: ShoshinNikita

One suggestion I have is to avoid getting bogged down in finding a “perfect” algorithm

Yeah, I agree. The linked CL already implements the "naive algorithm" I described in the issue description