如何mock K8s runtime.Object与Encoder为GetModified函数编写单元测试
两种方案实现入参模拟
方案1:使用K8s原生对象+官方Codec(最简便,推荐)
你不需要自己手写mock,K8s官方提供的核心资源对象(比如Pod、ConfigMap)本身就实现了runtime.Object接口,官方也有现成的runtime.Encoder实现,直接用即可,测试逻辑更贴合实际使用场景。
所需依赖导入
import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer/json" )
测试参数构造示例
// 1. 构造测试用runtime.Object:这里用最简单的ConfigMap为例 testObj := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: "test-cm", Annotations: map[string]string{ corev1.LastAppliedConfigAnnotation: `{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"test-cm"},"data":{"foo":"bar"}}`, "other-anno": "test-value", }, }, Data: map[string]string{ "foo": "bar", }, } // 2. 构造测试用runtime.Encoder:用官方JSON序列化器 scheme := runtime.NewScheme() _ = corev1.AddToScheme(scheme) codec := json.NewSerializerWithOptions(json.DefaultMetaFactory, scheme, scheme, json.SerializerOptions{Pretty: false})
补全后的完整测试代码
把上面的参数填入你的测试用例即可,示例:
func TestGetModified(t *testing.T) { // 预先构造公共测试资源 scheme := runtime.NewScheme() _ = corev1.AddToScheme(scheme) codec := json.NewSerializerWithOptions(json.DefaultMetaFactory, scheme, scheme, json.SerializerOptions{Pretty: false}) testCMWithoutAnno := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{Name: "test-cm-1"}, Data: map[string]string{"foo": "bar"}, } testCMWithAnno := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: "test-cm-2", Annotations: map[string]string{ corev1.LastAppliedConfigAnnotation: "old-config", }, }, Data: map[string]string{"foo": "new-bar"}, } type args struct { obj runtime.Object annotate bool codec runtime.Encoder } tests := []struct { name string args args wantErr bool }{ { name: "无注解+不开启annotate", args: args{ obj: testCMWithoutAnno.DeepCopyObject(), annotate: false, codec: codec, }, wantErr: false, }, { name: "有原始注解+开启annotate", args: args{ obj: testCMWithAnno.DeepCopyObject(), annotate: true, codec: codec, }, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := GetModified(tt.args.obj, tt.args.annotate, tt.args.codec) if (err != nil) != tt.wantErr { t.Errorf("GetModified() error = %v, wantErr %v", err, tt.wantErr) return } if err == nil && len(got) == 0 { t.Error("GetModified()返回空字节数组") } // 可额外断言返回的字节数组里包含预期的字段,比如Data的内容 }) } }
方案2:纯Mock接口实现(无额外依赖)
如果你需要完全隔离K8s资源依赖,可以用mock工具(比如gomock)生成runtime.Object和runtime.Encoder的mock实现,手动打桩方法返回值即可:
runtime.Object需要实现的方法很少,你也可以手写一个结构体实现接口,只需要满足metadataAccessor的调用要求即可,重点实现Annotations、SetAnnotations相关的逻辑。runtime.Encoder只需要实现Encode方法,返回固定的测试字节数组即可。
内容的提问来源于stack exchange,提问作者Jenney
相关产品推荐
相关产品推荐

