@@ -80,11 +80,77 @@ make unit TEST_PACKAGES="./pkg/controllers/machinesync/..." # Specific package
8080Prefer using ` GINKGO_EXTRA_ARGS` to pass additional arguments to ginkgo. Use ` GINKGO_ARGS` when you need to override the default values entirely.
8181
8282# ## Test Patterns
83- - Use ** Ginkgo/Gomega** framework
84- - Use ** Komega** for async assertions
85- - Use ** WithTransform** + helper functions
86- - Provide detailed error context
87- - Check existing test patterns before writing new ones
83+
84+ # ### Ginkgo/Gomega Best Practices
85+ Use ** Ginkgo/Gomega** framework and prefer built-in features over custom implementations:
86+ - Use ` DescribeTable` with ` Entry` for table-driven tests instead of manual loops
87+ - Use ` HaveField` , ` HaveValue` , ` HaveKey` for struct/map assertions instead of manual field checks
88+ - Use ` ConsistOf` for unordered slice matching instead of sorting + ` Equal`
89+ - Use ` MatchError` for error checking instead of string contains
90+ - Use ` BeNumerically` for numeric comparisons instead of manual range checks
91+
92+ # ### Async Assertions with Komega
93+ Use ** Komega** for Kubernetes object assertions:
94+ ` ` ` go
95+ // Use komega.Object for async assertions
96+ Eventually(k.Object(myResource)).Should(HaveField(" ObjectMeta.ResourceVersion" , Equal(expectedRV)))
97+
98+ // Update resources with komega helpers
99+ Eventually(k.UpdateStatus(myResource, func () {
100+ myResource.Status.SomeField = " value"
101+ })).Should(Succeed())
102+ ` ` `
103+
104+ # ### Test Organization
105+ - ** Nested Contexts** : Organize related test scenarios with nested ` Context()` blocks
106+ ` ` ` go
107+ Context(" when migrating from MachineAPI to ClusterAPI" , func () {
108+ Context(" when status is not paused" , func () {
109+ // Test cases
110+ })
111+ })
112+ ` ` `
113+ - ** Descriptive test names** : Use " should..." format: ` It(" should do nothing" , func () {...})`
114+ - ** Use ` By()` for test steps** : Document test phases with ` By(" Setting up namespaces for the test" )`
115+
116+ # ### Resource Management
117+ - ** Resource builders** : Use testutils resource builders for creating test objects
118+ ` ` ` go
119+ mapiMachine = mapiMachineBuilder.
120+ WithNamespace(namespace).
121+ WithName(" foo" ).
122+ WithAuthoritativeAPI(machinev1beta1.MachineAuthorityMachineAPI).
123+ Build ()
124+ ` ` `
125+ - ** Standard cleanup** : Use ` testutils.CleanupResources()` in AfterEach
126+ ` ` ` go
127+ testutils.CleanupResources(Default, ctx, cfg, k8sClient, namespace,
128+ & machinev1beta1.Machine{},
129+ & clusterv1.Machine{},
130+ )
131+ ` ` `
132+
133+ # ### Assertions
134+ - ** Complex assertions** : Combine matchers with ` SatisfyAll`
135+ ` ` ` go
136+ Eventually(komega.Object(resource)).Should(SatisfyAll(
137+ HaveField(" Status.AuthoritativeAPI" , Equal(expected)),
138+ HaveField(" Status.SynchronizedGeneration" , BeZero ()),
139+ ))
140+ ` ` `
141+ - ** Checking absence** : Use ` ShouldNot` with appropriate matchers
142+ ` ` ` go
143+ Eventually(komega.Object(resource)).ShouldNot(
144+ HaveField(" ObjectMeta.Annotations" , ContainElement(HaveKeyWithValue(key, value))))
145+ ` ` `
146+ - ** Nested field checks** : Chain ` HaveField` for nested assertions
147+ ` ` ` go
148+ HaveField(" Status.Conditions" , ContainElement(SatisfyAll(
149+ HaveField(" Type" , Equal(" Paused" )),
150+ HaveField(" Status" , Equal(corev1.ConditionTrue)),
151+ )))
152+ ` ` `
153+
88154
89155# ## Focused Testing
90156` ` ` go
0 commit comments