Skip to content

Commit fa9f70c

Browse files
authored
📖 cleanup: improves cronjob_controller_test.go gomega assertions (#4142)
Replaced some bespoke Eventually() signatures (e.g. func() bool and func() (int, error)) with the standard func(g Gomega), allowing gomega assertions to happen inside the Eventually(). Generally you _expect_ something _to_ happen and _eventually_, something _should_ happen. This is a part of #4135
1 parent e3ebfaf commit fa9f70c

File tree

2 files changed

+44
-66
lines changed

2 files changed

+44
-66
lines changed

docs/book/src/cronjob-tutorial/testdata/project/internal/controller/cronjob_controller_test.go

Lines changed: 22 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ import (
2929
"reflect"
3030
"time"
3131

32-
. "github.com/onsi/ginkgo/v2"
33-
. "github.com/onsi/gomega"
32+
. "github.com/onsi/ginkgo/v2"
33+
. "github.com/onsi/gomega"
3434
batchv1 "k8s.io/api/batch/v1"
3535
v1 "k8s.io/api/core/v1"
3636
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -96,14 +96,14 @@ var _ = Describe("CronJob controller", func() {
9696
},
9797
},
9898
}
99-
Expect(k8sClient.Create(ctx, cronJob)).Should(Succeed())
99+
Expect(k8sClient.Create(ctx, cronJob)).To(Succeed())
100100

101101
/*
102102
After creating this CronJob, let's check that the CronJob's Spec fields match what we passed in.
103103
Note that, because the k8s apiserver may not have finished creating a CronJob after our `Create()` call from earlier, we will use Gomega’s Eventually() testing function instead of Expect() to give the apiserver an opportunity to finish creating our CronJob.
104104
105105
`Eventually()` will repeatedly run the function provided as an argument every interval seconds until
106-
(a) the function’s output matches what’s expected in the subsequent `Should()` call, or
106+
(a) the assertions done by the passed-in `Gomega` succeed, or
107107
(b) the number of attempts * interval period exceed the provided timeout value.
108108
109109
In the examples below, timeout and interval are Go Duration values of our choosing.
@@ -113,12 +113,11 @@ var _ = Describe("CronJob controller", func() {
113113
createdCronjob := &cronjobv1.CronJob{}
114114

115115
// We'll need to retry getting this newly created CronJob, given that creation may not immediately happen.
116-
Eventually(func() bool {
117-
err := k8sClient.Get(ctx, cronjobLookupKey, createdCronjob)
118-
return err == nil
119-
}, timeout, interval).Should(BeTrue())
116+
Eventually(func(g Gomega) {
117+
g.Expect(k8sClient.Get(ctx, cronjobLookupKey, createdCronjob)).To(Succeed())
118+
}, timeout, interval).Should(Succeed())
120119
// Let's make sure our Schedule string value was properly converted/handled.
121-
Expect(createdCronjob.Spec.Schedule).Should(Equal("1 * * * *"))
120+
Expect(createdCronjob.Spec.Schedule).To(Equal("1 * * * *"))
122121
/*
123122
Now that we've created a CronJob in our test cluster, the next step is to write a test that actually tests our CronJob controller’s behavior.
124123
Let’s test the CronJob controller’s logic responsible for updating CronJob.Status.Active with actively running jobs.
@@ -128,13 +127,10 @@ var _ = Describe("CronJob controller", func() {
128127
We use Gomega's `Consistently()` check here to ensure that the active job count remains 0 over a duration of time.
129128
*/
130129
By("By checking the CronJob has zero active Jobs")
131-
Consistently(func() (int, error) {
132-
err := k8sClient.Get(ctx, cronjobLookupKey, createdCronjob)
133-
if err != nil {
134-
return -1, err
135-
}
136-
return len(createdCronjob.Status.Active), nil
137-
}, duration, interval).Should(Equal(0))
130+
Consistently(func(g Gomega) {
131+
g.Expect(k8sClient.Get(ctx, cronjobLookupKey, createdCronjob)).To(Succeed())
132+
g.Expect(createdCronjob.Status.Active).To(HaveLen(0))
133+
}, duration, interval).Should(Succeed())
138134
/*
139135
Next, we actually create a stubbed Job that will belong to our CronJob, as well as its downstream template specs.
140136
We set the Job's status's "Active" count to 2 to simulate the Job running two pods, which means the Job is actively running.
@@ -171,31 +167,24 @@ var _ = Describe("CronJob controller", func() {
171167

172168
controllerRef := metav1.NewControllerRef(createdCronjob, gvk)
173169
testJob.SetOwnerReferences([]metav1.OwnerReference{*controllerRef})
174-
Expect(k8sClient.Create(ctx, testJob)).Should(Succeed())
175-
// Note that you can not manage the status values while creating the resource.
176-
// The status field is managed separately to reflect the current state of the resource.
170+
Expect(k8sClient.Create(ctx, testJob)).To(Succeed())
171+
// Note that you can not manage the status values while creating the resource.
172+
// The status field is managed separately to reflect the current state of the resource.
177173
// Therefore, it should be updated using a PATCH or PUT operation after the resource has been created.
178-
// Additionally, it is recommended to use StatusConditions to manage the status. For further information see:
174+
// Additionally, it is recommended to use StatusConditions to manage the status. For further information see:
179175
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
180176
testJob.Status.Active = 2
181-
Expect(k8sClient.Status().Update(ctx, testJob)).Should(Succeed())
177+
Expect(k8sClient.Status().Update(ctx, testJob)).To(Succeed())
182178
/*
183179
Adding this Job to our test CronJob should trigger our controller’s reconciler logic.
184180
After that, we can write a test that evaluates whether our controller eventually updates our CronJob’s Status field as expected!
185181
*/
186182
By("By checking that the CronJob has one active Job")
187-
Eventually(func() ([]string, error) {
188-
err := k8sClient.Get(ctx, cronjobLookupKey, createdCronjob)
189-
if err != nil {
190-
return nil, err
191-
}
192-
193-
names := []string{}
194-
for _, job := range createdCronjob.Status.Active {
195-
names = append(names, job.Name)
196-
}
197-
return names, nil
198-
}, timeout, interval).Should(ConsistOf(JobName), "should list our active job %s in the active jobs list in status", JobName)
183+
Eventually(func(g Gomega) {
184+
g.Expect(k8sClient.Get(ctx, cronjobLookupKey, createdCronjob)).To(Succeed(), "should GET the CronJob")
185+
g.Expect(createdCronjob.Status.Active).To(HaveLen(1), "should have exactly one active job")
186+
g.Expect(createdCronjob.Status.Active[0].Name).To(Equal(JobName), "the wrong job is active")
187+
}, timeout, interval).Should(Succeed(), "should list our active job %s in the active jobs list in status", JobName)
199188
})
200189
})
201190

hack/docs/internal/cronjob-tutorial/writing_tests_controller.go

Lines changed: 22 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ import (
4747
"reflect"
4848
"time"
4949
50-
. "github.com/onsi/ginkgo/v2"
51-
. "github.com/onsi/gomega"
50+
. "github.com/onsi/ginkgo/v2"
51+
. "github.com/onsi/gomega"
5252
batchv1 "k8s.io/api/batch/v1"
5353
v1 "k8s.io/api/core/v1"
5454
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -114,15 +114,15 @@ var _ = Describe("CronJob controller", func() {
114114
},
115115
},
116116
}
117-
Expect(k8sClient.Create(ctx, cronJob)).Should(Succeed())
117+
Expect(k8sClient.Create(ctx, cronJob)).To(Succeed())
118118
119119
/*
120120
After creating this CronJob, let's check that the CronJob's Spec fields match what we passed in.
121121
Note that, because the k8s apiserver may not have finished creating a CronJob after our` + " `" + `Create()` + "`" + ` call from earlier, we will use Gomega’s Eventually() testing function instead of Expect() to give the apiserver an opportunity to finish creating our CronJob.` + `
122122
123123
` +
124124
"`" + `Eventually()` + "`" + ` will repeatedly run the function provided as an argument every interval seconds until
125-
(a) the function’s output matches what’s expected in the subsequent` + " `" + `Should()` + "`" + ` call, or
125+
(a) the assertions done by the passed-in ` + "`" + `Gomega` + "`" + ` succeed, or
126126
(b) the number of attempts * interval period exceed the provided timeout value.
127127
128128
In the examples below, timeout and interval are Go Duration values of our choosing.
@@ -132,12 +132,11 @@ var _ = Describe("CronJob controller", func() {
132132
createdCronjob := &cronjobv1.CronJob{}
133133
134134
// We'll need to retry getting this newly created CronJob, given that creation may not immediately happen.
135-
Eventually(func() bool {
136-
err := k8sClient.Get(ctx, cronjobLookupKey, createdCronjob)
137-
return err == nil
138-
}, timeout, interval).Should(BeTrue())
135+
Eventually(func(g Gomega) {
136+
g.Expect(k8sClient.Get(ctx, cronjobLookupKey, createdCronjob)).To(Succeed())
137+
}, timeout, interval).Should(Succeed())
139138
// Let's make sure our Schedule string value was properly converted/handled.
140-
Expect(createdCronjob.Spec.Schedule).Should(Equal("1 * * * *"))
139+
Expect(createdCronjob.Spec.Schedule).To(Equal("1 * * * *"))
141140
/*
142141
Now that we've created a CronJob in our test cluster, the next step is to write a test that actually tests our CronJob controller’s behavior.
143142
Let’s test the CronJob controller’s logic responsible for updating CronJob.Status.Active with actively running jobs.
@@ -147,13 +146,10 @@ var _ = Describe("CronJob controller", func() {
147146
We use Gomega's` + " `" + `Consistently()` + "`" + ` check here to ensure that the active job count remains 0 over a duration of time.
148147
*/
149148
By("By checking the CronJob has zero active Jobs")
150-
Consistently(func() (int, error) {
151-
err := k8sClient.Get(ctx, cronjobLookupKey, createdCronjob)
152-
if err != nil {
153-
return -1, err
154-
}
155-
return len(createdCronjob.Status.Active), nil
156-
}, duration, interval).Should(Equal(0))
149+
Consistently(func(g Gomega) {
150+
g.Expect(k8sClient.Get(ctx, cronjobLookupKey, createdCronjob)).To(Succeed())
151+
g.Expect(createdCronjob.Status.Active).To(HaveLen(0))
152+
}, duration, interval).Should(Succeed())
157153
/*
158154
Next, we actually create a stubbed Job that will belong to our CronJob, as well as its downstream template specs.
159155
We set the Job's status's "Active" count to 2 to simulate the Job running two pods, which means the Job is actively running.
@@ -190,31 +186,24 @@ var _ = Describe("CronJob controller", func() {
190186
191187
controllerRef := metav1.NewControllerRef(createdCronjob, gvk)
192188
testJob.SetOwnerReferences([]metav1.OwnerReference{*controllerRef})
193-
Expect(k8sClient.Create(ctx, testJob)).Should(Succeed())
194-
// Note that you can not manage the status values while creating the resource.
195-
// The status field is managed separately to reflect the current state of the resource.
189+
Expect(k8sClient.Create(ctx, testJob)).To(Succeed())
190+
// Note that you can not manage the status values while creating the resource.
191+
// The status field is managed separately to reflect the current state of the resource.
196192
// Therefore, it should be updated using a PATCH or PUT operation after the resource has been created.
197-
// Additionally, it is recommended to use StatusConditions to manage the status. For further information see:
193+
// Additionally, it is recommended to use StatusConditions to manage the status. For further information see:
198194
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
199195
testJob.Status.Active = 2
200-
Expect(k8sClient.Status().Update(ctx, testJob)).Should(Succeed())
196+
Expect(k8sClient.Status().Update(ctx, testJob)).To(Succeed())
201197
/*
202198
Adding this Job to our test CronJob should trigger our controller’s reconciler logic.
203199
After that, we can write a test that evaluates whether our controller eventually updates our CronJob’s Status field as expected!
204200
*/
205201
By("By checking that the CronJob has one active Job")
206-
Eventually(func() ([]string, error) {
207-
err := k8sClient.Get(ctx, cronjobLookupKey, createdCronjob)
208-
if err != nil {
209-
return nil, err
210-
}
211-
212-
names := []string{}
213-
for _, job := range createdCronjob.Status.Active {
214-
names = append(names, job.Name)
215-
}
216-
return names, nil
217-
}, timeout, interval).Should(ConsistOf(JobName), "should list our active job %s in the active jobs list in status", JobName)
202+
Eventually(func(g Gomega) {
203+
g.Expect(k8sClient.Get(ctx, cronjobLookupKey, createdCronjob)).To(Succeed(), "should GET the CronJob")
204+
g.Expect(createdCronjob.Status.Active).To(HaveLen(1), "should have exactly one active job")
205+
g.Expect(createdCronjob.Status.Active[0].Name).To(Equal(JobName), "the wrong job is active")
206+
}, timeout, interval).Should(Succeed(), "should list our active job %s in the active jobs list in status", JobName)
218207
})
219208
})
220209

0 commit comments

Comments
 (0)