Skip to content

Commit 57b06ba

Browse files
committed
Enhance AGENTS.md with more test patterns
1 parent 3416bfc commit 57b06ba

File tree

1 file changed

+71
-5
lines changed

1 file changed

+71
-5
lines changed

AGENTS.md

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,77 @@ make unit TEST_PACKAGES="./pkg/controllers/machinesync/..." # Specific package
8080
Prefer 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

Comments
 (0)