如何在不Mock FakeClient的Update方法时使其返回K8s对象更新错误
不用Mock让controller-runtime FakeClient的Update方法返回错误
controller-runtime的FakeClient遵循Kubernetes的API语义,我们可以利用它本身的行为逻辑,无需Mock就能让Update方法返回错误,核心思路是触发它的内置错误场景:
方法一:更新不存在的对象(返回NotFound错误)
当调用Update的对象不在FakeClient的缓存中时,它会直接返回NotFound错误。只需在初始化FakeClient时,不将待更新的对象加入缓存即可:
修改后的test.go代码
import ( "context" "testing" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/scheme" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" "github.com/stretchr/testify/assert" appsv1 "k8s.io/api/apps/v1" ) type fakeData struct { dep *appsv1.Deployment } func (d fakeData) updateDeployment(c client.Client) error { // 修正原代码笔误:使用d.dep而非未定义的dep return c.Update(context.Background(), d.dep) } func TestUpdateDeploymentFailed_NotFound(t *testing.T){ // 创建待更新的Deployment,不加入FakeClient缓存 dep := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: "test-dep", Namespace: "default", }, } fd := fakeData{ dep : dep, } // FakeClient初始化时不包含该Deployment fakeClient := fake.NewClientBuilder().WithScheme(scheme.Scheme).Build() err := fd.updateDeployment(fakeClient) // 验证返回NotFound错误 assert.True(t, errors.IsNotFound(err)) }
方法二:利用乐观锁冲突(返回Conflict错误)
FakeClient实现了Kubernetes的乐观并发控制:当待更新对象的ResourceVersion与缓存中的版本不一致时,会返回Conflict错误。我们可以通过构造版本不匹配的对象来触发这个场景:
修改后的test.go代码
import ( "context" "testing" "ptr" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/scheme" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" "github.com/stretchr/testify/assert" appsv1 "k8s.io/api/apps/v1" ) type fakeData struct { dep *appsv1.Deployment } func (d fakeData) updateDeployment(c client.Client) error { return c.Update(context.Background(), d.dep) } func TestUpdateDeploymentFailed_Conflict(t *testing.T){ // 初始化一个带有ResourceVersion的Deployment,加入FakeClient缓存 cachedDep := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: "test-dep", Namespace: "default", ResourceVersion: "1", // 缓存中的版本 }, } // 创建待更新的Deployment,使用相同标识但版本不匹配 updateDep := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: "test-dep", Namespace: "default", ResourceVersion: "2", // 与缓存版本不一致 }, Spec: appsv1.DeploymentSpec{ Replicas: ptr.To(int32(2)), // 任意修改Spec内容 }, } fd := fakeData{ dep : updateDep, } // FakeClient初始化时加入缓存的Deployment fakeClient := fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(cachedDep).Build() err := fd.updateDeployment(fakeClient) // 验证返回Conflict错误 assert.True(t, errors.IsConflict(err)) }
注意事项
- 原
data.go中存在笔误:updateDeployment方法里应该使用d.dep而非未定义的dep,否则编译会报错。 - 需要导入
k8s.io/apimachinery/pkg/api/errors包来准确判断错误类型。
内容的提问来源于stack exchange,提问作者Hossain Mahmud
相关产品推荐
相关产品推荐

