You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 16:48:25