Skip to content
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

Filter tests are fragile and hard to understand #11777

Closed
johananl opened this issue Jan 31, 2025 · 17 comments
Closed

Filter tests are fragile and hard to understand #11777

johananl opened this issue Jan 31, 2025 · 17 comments
Labels
kind/cleanup Categorizes issue or PR as related to cleaning up code, process, or technical debt. needs-priority Indicates an issue lacks a `priority/foo` label and requires one. needs-triage Indicates an issue or PR lacks a `triage/foo` label and requires one.

Comments

@johananl
Copy link
Member

johananl commented Jan 31, 2025

While working on a change in the bootstrap API I've encountered tests such as the following:

g.Expect(diff).To(BeComparableTo(`&v1beta1.KubeadmConfigSpec{
ClusterConfiguration: nil,
InitConfiguration: &v1beta1.InitConfiguration{
TypeMeta: {},
BootstrapTokens: nil,
NodeRegistration: v1beta1.NodeRegistrationOptions{
- Name: "",
+ Name: "A new name",
CRISocket: "",
Taints: nil,
... // 4 identical fields
},
LocalAPIEndpoint: {},
SkipPhases: nil,
Patches: nil,
},
JoinConfiguration: nil,
Files: nil,
... // 10 identical fields
}`))

When such tests failed for me I found it very tricky to figure out what's wrong. This is because the tests check the correctness of a diff value that's returned as a string from the function under test and prints any mismatches to the developer as a diff of two diffs (returned string vs expected string). Here is a sample failure:

--- FAIL: TestMatchInitOrJoinConfiguration (0.00s)
    --- FAIL: TestMatchInitOrJoinConfiguration/returns_false_if_InitConfiguration_is_NOT_equal (0.00s)
        filters_test.go:587:
            Expected object to be comparable, diff:   (
                """
                ... // 17 identical lines
                      JoinConfiguration:  nil,
                      PreKubeadmCommands: nil,
            -         ... // 2 identical fields
                    },
                    Files:     nil,
                    DiskSetup: nil,
            -       ... // 6 identical fields
            +       ... // 9 identical fields
                  }
                """
              )
FAIL

When the expected diff string contains a difference (namely lines with +/-), the output gets even more confusing since we now have two "layers" of diffs -- the diff representation in the two diff strings (expected and actual) and the diff between the two diff strings.

In addition, the tests are sensitive to whitespace differences because we rely on string comparison rather than semantic equivalence of two data structures.

I assume there was a good reason for writing these tests like this in the first place. However, I wonder if we can simplify these, get rid of relying on comparing strings somehow or find another way to make these tests less fragile and easier to debug.

cc @sbueringer

@k8s-ci-robot k8s-ci-robot added needs-priority Indicates an issue lacks a `priority/foo` label and requires one. needs-kind Indicates a PR lacks a `kind/foo` label and requires one. needs-triage Indicates an issue or PR lacks a `triage/foo` label and requires one. labels Jan 31, 2025
@k8s-ci-robot
Copy link
Contributor

This issue is currently awaiting triage.

If CAPI contributors determine this is a relevant issue, they will accept it by applying the triage/accepted label and provide further guidance.

The triage/accepted label can be added by org members by writing /triage accepted in a comment.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@johananl
Copy link
Member Author

/kind cleanup

@k8s-ci-robot k8s-ci-robot added kind/cleanup Categorizes issue or PR as related to cleaning up code, process, or technical debt. and removed needs-kind Indicates a PR lacks a `kind/foo` label and requires one. labels Jan 31, 2025
@sbueringer
Copy link
Member

What would you suggest?

@johananl
Copy link
Member Author

I don't have a concrete suggestion. As I wrote above I assume these tests have been written like this because there was no easier/simpler solution.

I think we can't compare structs in the tests because the diff string is the object which the function under test must return. Right?

@johananl
Copy link
Member Author

I wonder if it's important to test the diff strings at all. Of course, we aren't unit testing the functionality of go-cmp here. Rather, we want to ensure functions such as matchInitOrJoinConfiguration() are implemented correctly. I guess a relevant question is if the implementation might break a part of a diff. IOW, maybe it's enough to test for diff empty vs. not empty?

@sbueringer
Copy link
Member

sbueringer commented Jan 31, 2025

These tests are verifying that the diff looks as expected and especially that we notice if anything changes in a way that affects these diffs, basically like a golden file (e.g. we add a new field to the KubeadmConfig CRD that would trigger rollouts after CAPI upgrade)

@johananl
Copy link
Member Author

Yes. I realize that testing the diff contents covers more potential problems than just the existence of a diff. The problem is that IMO the current state is very fragile.

By the way, the go-cmp docs say the following:

Do not depend on this output being stable. If you need the ability to programmatically interpret the difference, consider using a custom Reporter.

I even vaguely remember the library randomizing tabs vs. spaces to actively prevent people from relying on stability there (not sure if that's still the case though).

@sbueringer
Copy link
Member

I even vaguely remember the library randomizing tabs vs. spaces to actively prevent people from relying on stability there (not sure if that's still the case though).

I would expect that our tests would not be stable if this would be the case.

@sbueringer
Copy link
Member

sbueringer commented Jan 31, 2025

In my opinion we don't depend on this output being stable. But we do want to know if anything changes, either because we bump the library or because we add fields to the CRDs

@johananl
Copy link
Member Author

johananl commented Jan 31, 2025

Relevant issue: google/go-cmp#344

I wonder what a custom reporter is. Maybe that's what gomega does and hence the tests don't fail at random.

@johananl
Copy link
Member Author

In any case, I opened this since I had a hard time fixing the broken tests while working on making some KubeadmConfig fields reusable. If you think there is nothing to improve here we can close 👍

@sbueringer
Copy link
Member

I don't think gomega injects a custom reporter into the go-cmp library behind our back :)

@johananl
Copy link
Member Author

I don't think gomega injects a custom reporter into the go-cmp library behind our back :)

Yeah, I couldn't find such a thing either. In the past I'm 90% sure I saw go-cmp replace tabs with spaces and vice versa at random to prevent me from programmatically comparing diffs.

@sbueringer
Copy link
Member

sbueringer commented Jan 31, 2025

Maybe BeComparableTo drops whitespace or something (and it uses cmp.Equal underneath)

We definitely use it because the output is better than Equal

@mboersma
Copy link
Contributor

mboersma commented Feb 3, 2025

https://pkg.go.dev/github.com/onsi/gomega#BeComparableTo

I was curious about the implementation, so here it is:
https://github.com/onsi/gomega/blob/36fbc8471a1a2391d40b9b8e561e014b3771255c/matchers/be_comparable_to_matcher.go#L16-L40
It basically just calls cmp.Equal after testing for nil and trying a shortcut for byte slices.

@sbueringer
Copy link
Member

Okay. I would say let's keep it as is

/close

@k8s-ci-robot
Copy link
Contributor

@sbueringer: Closing this issue.

In response to this:

Okay. I would say let's keep it as is

/close

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/cleanup Categorizes issue or PR as related to cleaning up code, process, or technical debt. needs-priority Indicates an issue lacks a `priority/foo` label and requires one. needs-triage Indicates an issue or PR lacks a `triage/foo` label and requires one.
Projects
None yet
Development

No branches or pull requests

4 participants