-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support running single test in a testify suite #25
Comments
Hm 🤔, I'm wondering what you are referring to when you say "suite". Neotest has a notion to run all tests of a project, which I believe is what is called a "suite". But it kind of sounds like you mean something very specific here (and not just all test functions of a project)? Exactly what is |
I think this is related to |
Yes, apologies, I mean a testify suite, or something similar. |
Ok, got it, thanks for this @jstensland and @thelooter ! I'm not sure, by just looking at the example below (from the testify docs), what could be the best way to attempt to initially support this. // Basic imports
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)
// Define the suite, and absorb the built-in basic suite
// functionality from testify - including a T() method which
// returns the current testing context
type ExampleTestSuite struct {
suite.Suite
VariableThatShouldStartAtFive int
}
// Make sure that VariableThatShouldStartAtFive is set to five
// before each test
func (suite *ExampleTestSuite) SetupTest() {
suite.VariableThatShouldStartAtFive = 5
}
// All methods that begin with "Test" are run as tests within a
// suite.
func (suite *ExampleTestSuite) TestExample() {
assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive)
suite.Equal(5, suite.VariableThatShouldStartAtFive)
}
// In order for 'go test' to run this suite, we need to create
// a normal test function and pass our suite to suite.Run
func TestExampleTestSuite(t *testing.T) {
suite.Run(t, new(ExampleTestSuite))
} I haven't used testify personally, but I would like to look into how it works by setting up a testify suite and play around with it. My gut feeling is though that this could lead to having to redesign the adapter into being able to handle multiple "modes" or similar, and the detection of said modes. Today we have testify, tomorrow we might have something different. Could get ugly if you mix them... I'm also wondering how far users are taking it with these testify suites. The more crazy you get with e.g. the receiver signature, the harder it could be to do the right thing and have bugs. Anyway, I'm mostly thinking out loud here. Would love any of your input/insights into this if you have any 😄 so I can take better decisions here on trying to support testify. @jstensland I'm away from my laptop until next week, but if I read this correctly, one solution would be to tweak the test detection and have all testify tests prefixed by its suite name. Meaning, instead of listing Also, right now, I believe |
I'm just leaving it here for now, but there are likely a lot of good insights posted on the neotest-go adapter:
But like I said, I would like to play around with this all and find a maintainable and robust solution. |
Starting with an example to try to make my statements more concrete. using that example
Go test output is like this
you can target a particular suite test with a
or with flags to testify like this
The former approach seems more general to me, and is where I started You're right that
This is only an issue because you're running the entire package test by test, right? Once packages are run all at once, you wouldn't have the double invocation. Those are the most relevant threads I have found. I see you have this code as well, which is why they get detected but the name is only the method, and does not include the Suite the way the go test output does.
This is where I stopped, since I don't know the neotest framework well. Can you not create a namespace for all the suite tests some how? I haven't figured out how the treesitter tags get interpreted yet, when discovering test positions. From a user point of view, I would expect the methods to be grouped under the suite, similar to test cases. Other thoughts
|
Hello everyone, I've investigated a bit this problem and want to share some results. The branch is here https://github.com/uroborosq/neotest-golang/tree/feat/testify_suites For now it's only extending treesitter query. I've add two like subqueries - first collect suite methods into namespace with name of standard go test function and second collects directly suite methods. So generally it works, I can run and debug suite method/subtests, but there is a list of limitations:
Mb there are some other possibilites, I am very new to lua and neovim) |
Thanks for having a look at this @uroborosq 😄 Hm, I wonder if this can work: I didn't know namespaces existed in Neovim, but apparently they do: https://github.com/nvim-neotest/neotest/blob/26ed90509c377d10dbdebd25b7094a886323b32b/lua/neotest/types/init.lua#L3-L9 The The position id for the individual test becomes something like this, which is also great: {
id = "/Users/fredrik/code/public/neotest-golang/tests/go/testify_test.go::ExampleTestSuite::TestExample",
name = "TestExample",
path = "/Users/fredrik/code/public/neotest-golang/tests/go/testify_test.go",
range = { 26, 0, 29, 1 },
type = "test"
} Right now, the test command won't come out proper and the test won't get executed, but that can be fixed. Also, there's a regex I haven't fully wrapped my head around what drawbacks/caveats there are here... EDIT: I had a bran fart. I will have to prepend EDIT 2: The neotest-python adapter "auguments" the AST-detected test names with runtime-collected data. It's possible this technique would be helpful here... EDIT 3: A potential solution would be to create a lookup table, which gets created right after test discovery. The known (potential) suite would be |
You you're determining the name func TestExampleTestSuite(t *testing.T) {
suite.Run(t, new(ExampleTestSuite))
} Taking a quick look through a few repos, I see the struct is often private The potential to be in a different file is a tricky edge case as well with how neotest is set up. Go considers all files in a package in scope. Does this mean given the neotest interface the adapter would need to parse the whole package for each file to have chance at a more deterministic route? As in, it would have to do a few queries to trace down the right Name space and suite runner test? Feel free to just point me at some docs. All I have found is the readme overview pointing to here, and I haven't had the chance to read through neotest. Perhaps another adapter could be an inspiration too. What other supported languages compile this way... 🤔 |
@jstensland I added some edits to my previous comment and I did not realize this until it was too late, but I got the namespace from the receiver ( I think that supporting test suites will be super difficult just in general, because I cannot run a But if we limit the scope to at least initially only support testify, it should be possible to do support this using tree-sitter AST detection;
I don't quite follow. You mean like this? func TestSuite(t *testing.T) {
suite.Run(t, new(privateStruct))
} The above should be totally fine.
Sorry, I don't think there are any. You have to install lazydev.nvim and dive into Neotest's codebase. You can take inspiration from neotest-python, neotest-zig, neotest-rust and other fairly recently developed adapters too. But anyway, have a look at You can parse code using any logic you want here. Could even drop to Go and run AST detection with Go to do certain things where the Neotest position tree structure isn't important, such as when creating a receiver/namespace lookup. |
From my side, here are two main problems:
For first one, we have two options:
So, generally first point looks doable. I think, that it might be OK to accept this limitation for different files for the initial support. But second one looks harder. As I understand, Neotest tree design has very restricted structure:
func TestProductService(t *testing.T) {
suite.Run(t, new(ProductServiceSuite))
}
type ProductServiceSuite struct {
suite.Suite
}
func (s *ProductServiceSuite) TestKEK() {
cases := []struct {
name string
err error
}{
{
name: "kek",
err: assert.AnError,
},
{
name: "lol",
},
}
for _, test := range cases {
s.Run(test.name, func() {
if test.err != nil {
s.Fail(test.err.Error())
}
})
}
}
func TestSmth(t *testing.T) {
t.Run("noname", func(t *testing.T) {
t.Fail()
})
t.Run("newname", func(t *testing.T) {
})
}
func (s *ProductServiceSuite) TestLOL() {
s.Run("first", func() {
const a = 4
if 1 != 2 || a == 4 {
s.Fail("oh no")
}
})
s.Run("kek", func() {
if 2 == 3 {
s.Fail("yes")
}
})
} There is one suite with two methods, which separated with one standard test function. With my queries, it produces the following neotest tree:
As you can see, there are two TestProductService namespaces, which are almost the same, except range. This produce the weird output, which I've posted later. |
@uroborosq I think there is a fairly simple solution to support multiple test files, by leveraging a receiver-testfunction lookup like I explained above/earlier. Do you see a caveat or drawback with this? For the second problem, I would have to tinker a bit with postprocessing of the positions returned by the Neotest/treesitter lib currently used. Either we can postprocess the position nodetree directly or create a supporting data structure. The latter could be less coupled, less disruptive and less problematic with future changes to Neotest but could also be noticeably slower. I am pretty confident I can solve this, but with the limitation of only supporting Testify (as this seems to follow a Having all this said, and as a side note, I would personally like to focus on getting some basic support in for running all tests in a file using one Supporting test suites will be tricky and I want to carefully decide where to add the complexity required. Quite frankly, this increased insight/understanding while exploring this topic so far has given me a reason not to ever want to use test suites (not limited to testify) in my own projects. I really wish |
Thanks for the responses. I am away for a bit but will come back and look more deeply later next week, although you're miles ahead 🙂. A few thoughts in case they can be helpful. It looks to me like Suite detection in VSCode go extension. Regex rather than tree sitter and I'm sure the internals of recording the tests are different, to the larger problems discussed. At least an example of cases they are trying to cover between the comment and regex |
A quick update on this topic. I just merged in the last stuff I wanted to make sure I got right for this adapter. It's basically at a mental v1.0.0 for me. 🎉 This means, unless there are bugs reported, testify suites (and suites in general) is the main enhancement I'll look into next. However, I also have a vacation coming up so I'm not going to make promises in terms of timeframes. 😄 |
This comment was marked as outdated.
This comment was marked as outdated.
Would be nice if you had the time to test drive this some in actual projects, and provide feedback! |
Tried testing with
|
So it looks like it choked on this line for you? -- neotest-golang/lua/neotest-golang/testify.lua:239
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, content) Hm, strange. I wonder why I'm not also getting that. I made basically the final touches on the overall functionality in the PR now, commit 7c720b4. If you pull the latest commit in the PR, do you still see this error? You're on LazyVim and on Neovim 0.10.0 stable? |
@nikosbatsaras @evgeniy-krivenko @thelooter @uroborosq @roveo @zankich sorry for the spam here, but if you think it's worth your time, please help me and have a look at #58 (in terms of trying it out) where I've implemented stretchr/testify support. Would like to gather problems, such as the one highlighted in the post just above this one, so to understand what works and what doesn't work before I start refactoring, cleanup and add some tests. |
@jstensland I was able to reproduce your problem in LazyVim and I believe I pushed a valid fix for it. Have another look, please! 😄 |
Issue is resolved 🎉 Poking around, things are working alright, but I do have one suite that's getting skipped and I don't see why. It's using the |
@jstensland I'm pretty sure you are hitting the same issue as @nikosbatsaras is hitting here: #58 (comment) Can you post what your test suite function looks like? |
Oh wait, you say you are already using that So, another issue right now is that I need to figure out how to trigger a rebuild of the lookup as you edit your code. It gets created only once right now, at adapter initialization. Could that be it? If you can provide a minimal example, I can dig into it. |
Most suites worked, and my example that doesn't work is in a private repo. I tried to recreate it and don't reproduce it. I'll see what I can do.
|
I wasn't reading the run spec close enough I guess. It doesn't produce the right suite namespace. It looks like the failing cases have another test after the suite in the file. To reproduce, put the following test at the bottom of
The go up to |
Very good find @jstensland - please try the latest commit in the branch. I pushed a fix for this: |
tested where I originally had the issues and looks fixed! thanks! |
Thanks for verifying @jstensland. So, the essential functionality is in place and I'm thinking I'll try and get what we have now merged but with some refactoring, polish and tests. So in the meantime, feel free to run neotest-golang with the |
I've been using this and it's quite nice. Thank you for all the work @fredrikaverpil |
Thanks for your feedback @jstensland ❤️ |
I just updated and caught that. thanks 👍 |
When running the nearest test in a suite, I would expect the run command to only run the specified test, but instead it quietly misses completely.
Expected run spec command -run option
Actual run spec command -run option
The additional surprise here is that when then actual command is run, there is no notification that no tests were matched, the summary suggested it worked, but the coverage wasn't updated.
This is when running the nearest test, looking at the run spec produced by
build_single_test_runspec
The text was updated successfully, but these errors were encountered: