Fixing PR tests in workflow-pr bot #106
Reference in New Issue
Block a user
Delete Branch "pr-tests"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
This is initial set of fixes for the unit tests. Will add additional tests to increase coverage.
I think the best to review here is just one commit at a time instead of entire set of changes. They are incremental.
Can be merged, but needs followup. Also some questions.
@@ -71,0 +147,4 @@gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockCreatePR, nil, true).AnyTimes()gitea.EXPECT().RequestReviews(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()For future: In tests with many expected functions, it is difficult to spot which mock expectation is important to the test and which ones are just to define required return values. What is the best way? I'm not sure. What do you think?
Either way it is probably a good idea to be explicit by using Times(0) (instead of no coded expectation) or Times(1) or a small count instead of AnyTimes for important ones.
https://pkg.go.dev/go.uber.org/mock@v0.6.0/gomock#Call should have a way to specify an explanation added to the error message for what an expectation tests, but it currently does not. E.g. it could support a reason("to ensure foo was tested") function on Call which would change the errors to read "expected call to ensure foo was tested at".
So for now what is an alternative. A comment above the important ones might be a good idea. But then I have seen comments above unimportant mocks, so it is still a better idea to separate those. Perhaps use an anonymous function assigned to an explaining variable name like unimportantMocking or a code block{} around them.
I suspect that some tests don't test the important parts from data that is set, but only which functions are called in the test case. Which may be a result from aiming for coverage. Forgetting to make tests useful or forgetting to test the important effects usually causes pain later. But I only found one where an important test might have been omitted, though to be sure if that is so or to find all I would have needed to review in more detail, so maybe my suspicion is in error.
I agree with you here. But the correct way of handling this would be to start the loop of,
When we start with case of no unit tests, any unit tests are better than nothing. But you are correct that too many mocks, mostly as result of code that is not doing one thing as it's a series of git commands, makes things confusing in the test.
In this particular snippet though, the use of AnyTimes() is sufficient, though ideally yes, it should be reduced to specific amount of calls. But this requires refactoring and removal of the if() paths.
While too many mocks make things harder, having fewer of them does not solve knowing when a mock is intended to test something by itself vs just enabling a later test.
So where is the test that this causes no action? Is it because the function for an action is not mocked? Then that should be made explicit by expecting it with Times(0).
Filed feature request for mock expect reason: https://github.com/uber-go/mock/issues/296
@@ -103,3 +75,1 @@}t.Run("PR sync request against PrjGit == no action", func(t *testing.T) {t.Run("PR_sync_request_against_PrjGit_==_no_action", func(t *testing.T) {Most of these _ are wrong. The LLM got confused by the error when running the test?
I suspect this is not testing the no action part, as there is no expect with Times(0).
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
The UpdatePullRequest() is not called, that's why it has "no action". It basically parses the PrjGit only PR and there's nothing to do.
By default, all mocks are Times(0) unless specified.
No, by default mocks are Times(1) and AnyTimes() overwrites that to 0-veryBigNumber, so here it allows UpdatePullRequest to be called.
To be more specific expected mocks by default are Times(1). You are right that those where EXPECT() is never called will fail the test if called, but here expect was called.
UpdatePullRequest() (Gitea mock) is never called here. This is not mocked in this instance and the other mocked function calls are all reading only, not updating anything (except for the SetRepoOptions, but that does it always and can be ignored)
So, it's unexepcted if this would be called here. "PR sync" test below is expected to call it, but here it is not since the updated PR is the project git PR, where we do not expect to update package PRs from it. Hence the "no action" in the title.
Or are you here simply mentioning that the _ in the tests names is not necessary? In that case, you'd be correct. I think it's added here simply for consistency, so you can find the test by copy-paste the failed test name.
@@ -0,0 +574,4 @@// CreatePullRequestIfNotExist returns isNew=truegitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(prjPR, nil, true).AnyTimes()// Expect SetLabels to be called for new PRgitea.EXPECT().SetLabels("test-org", gomock.Any(), int64(10), gomock.Any()).Return(nil, nil).AnyTimes()Should be Times(1). Possibly compare that the right the label is set?
@@ -0,0 +680,4 @@{PR: pkgPR},},}_ = prset // Suppress unused for now if it's really unused, but it's likely used by common.FetchPRSet internally if we weren't mocking everythingI don't think that is possible? LLM or human mistake? If I didn't miss anything delete it.
This is LLM comment why prset is unused. It's not used because it's re-created internally from the PR body text. So this prset here is not returned by any of the mocks or used by them.
LLM pasted it in here and the was, I guess, surprised that it's not used 😉 but it's harmless here.
@@ -0,0 +725,4 @@{PR: prjPR},},}_ = prsetSame as above.
@@ -0,0 +862,4 @@gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()The commit message says "PR events correctly trigger processing", but as all these allow anything, so do these actually do that?
This looks like a smoke test that different message formats either do something or return an error. The last test here expects an error if the message format is unknown.
@@ -269,3 +259,3 @@git: git,}*/process.EXPECT().Process(gomock.Any(), gomock.Any(), gomock.Any())// process.EXPECT().Process(gomock.Any())For future improvement: This and the surrounding comments should make clear why the code is in a comment here.
Thanks for the review. Quite a bit of functionality for PR processing happens in the /common code which have actual tests there. In the code here we have either a lot of git operations or it's calling the other routines in /common.
Because of a lot of mocking, it was rather tedious to create almost any unit tests here, so LLM helps. Eventually, this code needs to be refactored and then the tests end up with fewer mocks and be more precise. But one step at a time.