envTestを使って、kubernetesのAPIを使うテストを行う

KubernetesのAPIを呼び出すコードを書いていてそこにロジックが含まれる時、テストを行いたい。こういう時、envTestを使うと手軽にetcdとapiserverを起動してテストできる。

ドキュメントは https://book.kubebuilder.io/reference/testing/envtest.html

まずはシンプルに動かしてみる。testEnv.Start() でetcdとapiserverを起動し、そのapiserverに繋がるrest.Configを得る。rest.Configを使ってkubernetes.Clientsetを作成し、ConfigMapを作成したり取得するというもの。テストの最後にはtestEnv.Stop()でetcdとapiserverを停止している。

package main

import (
	"fmt"
	"github.com/k0kubun/pp"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"sigs.k8s.io/controller-runtime/pkg/envtest"
	"testing"
)

func Test_envtest(t *testing.T) {
	testEnv := &envtest.Environment{}

	restConfig, err := testEnv.Start()
	if err != nil {
		t.Error(err)
	}

	clientset, err := kubernetes.NewForConfig(restConfig)
	if err != nil {
		t.Error(err)
	}

	cm := &v1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-configmap",
			Namespace: "default",
		},
		Data: map[string]string{
			"foo": "bar",
		},
	}

	fmt.Printf("cm: %s\n", pp.Sprint(cm))
	_, err = clientset.CoreV1().ConfigMaps("default").Create(cm)
	if err != nil {
		t.Error(err)
	}
	got, err := clientset.CoreV1().ConfigMaps("default").Get("test-configmap", metav1.GetOptions{})
	if err != nil {
		t.Error(err)
	}

	fmt.Printf("got: %s\n", pp.Sprint(got))
	if got.Name != cm.Name {
		t.Errorf("missmatch: got = %v, want = %v", got.Name, cm.Name)
	}

	err = testEnv.Stop()
	if err != nil {
		t.Error(err)
	}
}

実行するとこのようになる。作成元のConfigMapと取得したConfigMapをそれぞれ出力しているが、UIDなどが付与されておりリソースとして登録されていることが確認できる。

$ go test ./testenv_test.go -v
=== RUN   Test_envtest
cm: &v1.ConfigMap{
  TypeMeta: v1.TypeMeta{
    Kind:       "",
    APIVersion: "",
  },
  ObjectMeta: v1.ObjectMeta{
    Name:              "test-configmap",
    GenerateName:      "",
    Namespace:         "default",
    SelfLink:          "",
    UID:               "",
    ResourceVersion:   "",
    Generation:        0,
    CreationTimestamp: v1.Time{
      Time: 1-01-01 00:00:00 UTC,
    },
    DeletionTimestamp:          (*v1.Time)(nil),
    DeletionGracePeriodSeconds: (*int64)(nil),
    Labels:                     map[string]string{},
    Annotations:                map[string]string{},
    OwnerReferences:            []v1.OwnerReference{},
    Initializers:               (*v1.Initializers)(nil),
    Finalizers:                 []string{},
    ClusterName:                "",
    ManagedFields:              []v1.ManagedFieldsEntry{},
  },
  Data: map[string]string{
    "foo": "bar",
  },
  BinaryData: map[string][]uint8{},
}
got: &v1.ConfigMap{
  TypeMeta: v1.TypeMeta{
    Kind:       "",
    APIVersion: "",
  },
  ObjectMeta: v1.ObjectMeta{
    Name:              "test-configmap",
    GenerateName:      "",
    Namespace:         "default",
    SelfLink:          "/api/v1/namespaces/default/configmaps/test-configmap",
    UID:               "478835ca-73df-11ea-b65a-acde48001122",
    ResourceVersion:   "43",
    Generation:        0,
    CreationTimestamp: v1.Time{
      Time: 2020-04-01 15:09:00 Local,
    },
    DeletionTimestamp:          (*v1.Time)(nil),
    DeletionGracePeriodSeconds: (*int64)(nil),
    Labels:                     map[string]string{},
    Annotations:                map[string]string{},
    OwnerReferences:            []v1.OwnerReference{},
    Initializers:               (*v1.Initializers)(nil),
    Finalizers:                 []string{},
    ClusterName:                "",
    ManagedFields:              []v1.ManagedFieldsEntry{},
  },
  Data: map[string]string{
    "foo": "bar",
  },
  BinaryData: map[string][]uint8{},
}
--- PASS: Test_envtest (6.17s)
PASS
ok      command-line-arguments  8.066s

client-goのパッケージはモッキングするのも大変だろうから、envtestを使っていくと良さそう。

Leave a Reply

Your email address will not be published. Required fields are marked *