diff --git a/api/v1alpha1/types_test.go b/api/v1alpha1/types_test.go
new file mode 100644
index 00000000..c7dc7ecb
--- /dev/null
+++ b/api/v1alpha1/types_test.go
@@ -0,0 +1,20 @@
+package v1alpha1
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/types"
+)
+
+func TestObjKey(t *testing.T) {
+	xlineCluster := XlineCluster{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "xline",
+			Namespace: "default",
+		},
+	}
+	expected_objkey := types.NamespacedName{Name: "xline", Namespace: "default"}
+	assert.Equal(t, xlineCluster.ObjKey(), expected_objkey)
+}
diff --git a/go.mod b/go.mod
index f9bc4ada..f2a74354 100644
--- a/go.mod
+++ b/go.mod
@@ -6,6 +6,7 @@ require (
 	github.com/go-logr/logr v1.2.4
 	github.com/onsi/ginkgo/v2 v2.11.0
 	github.com/onsi/gomega v1.27.10
+	github.com/stretchr/testify v1.8.4
 	k8s.io/api v0.28.3
 	k8s.io/apimachinery v0.28.3
 	k8s.io/client-go v0.28.3
@@ -41,6 +42,7 @@ require (
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
 	github.com/pkg/errors v0.9.1 // indirect
+	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/prometheus/client_golang v1.16.0 // indirect
 	github.com/prometheus/client_model v0.4.0 // indirect
 	github.com/prometheus/common v0.44.0 // indirect
diff --git a/go.sum b/go.sum
index 8e8882fc..aeb78d3e 100644
--- a/go.sum
+++ b/go.sum
@@ -109,7 +109,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
diff --git a/internal/reconciler/cluster_reconciler.go b/internal/reconciler/cluster_reconciler.go
index a76f641a..f290d5d0 100644
--- a/internal/reconciler/cluster_reconciler.go
+++ b/internal/reconciler/cluster_reconciler.go
@@ -35,16 +35,15 @@ type XlineClusterReconciler struct {
 type ClusterStageRecResult struct {
 	Stage  xapi.XlineClusterOprStage
 	Status xapi.OprStageStatus
-	Action xapi.OprStageAction
 	Err    error
 }
 
-func clusterStageSucc(stage xapi.XlineClusterOprStage, action xapi.OprStageAction) ClusterStageRecResult {
-	return ClusterStageRecResult{Stage: stage, Status: xapi.StageResultSucceeded, Action: action}
+func clusterStageSucc(stage xapi.XlineClusterOprStage) ClusterStageRecResult {
+	return ClusterStageRecResult{Stage: stage, Status: xapi.StageResultSucceeded}
 }
 
-func clusterStageFail(stage xapi.XlineClusterOprStage, action xapi.OprStageAction, err error) ClusterStageRecResult {
-	return ClusterStageRecResult{Stage: stage, Status: xapi.StageResultFailed, Action: action, Err: err}
+func clusterStageFail(stage xapi.XlineClusterOprStage, err error) ClusterStageRecResult {
+	return ClusterStageRecResult{Stage: stage, Status: xapi.StageResultFailed, Err: err}
 }
 
 // Reconcile all sub components
@@ -69,18 +68,16 @@ func (r *ClusterStageRecResult) AsXlineClusterRecStatus() xapi.XlineClusterRecSt
 
 // reconcile xline cluster resources.
 func (r *XlineClusterReconciler) recXlineResources() ClusterStageRecResult {
-	action := xapi.StageActionApply
-
 	// create a xline service
 	service := tran.MakeService(r.CR, r.Schema)
 	if err := r.CreateOrUpdate(service, &corev1.Service{}); err != nil {
-		return clusterStageFail(xapi.StageXlineService, action, err)
+		return clusterStageFail(xapi.StageXlineService, err)
 	}
 	// create a xline statefulset
 	statefulSet := tran.MakeStatefulSet(r.CR, r.Schema)
 	if err := r.CreateOrUpdate(statefulSet, &appv1.StatefulSet{}); err != nil {
-		return clusterStageFail(xapi.StageXlineStatefulSet, action, err)
+		return clusterStageFail(xapi.StageXlineStatefulSet, err)
 	}
-	return clusterStageSucc(xapi.StageComplete, action)
+	return clusterStageSucc(xapi.StageComplete)
 
 }
diff --git a/internal/reconciler/cluster_sync.go b/internal/reconciler/cluster_sync.go
index 00cc19b0..a2d0a889 100644
--- a/internal/reconciler/cluster_sync.go
+++ b/internal/reconciler/cluster_sync.go
@@ -27,7 +27,6 @@ func (r *XlineClusterReconciler) syncXlineStatus(xlineStatus *xapi.XlineClusterS
 	}
 
 	stsRef := tran.GetStatefulSetKey(r.CR.ObjKey())
-	// collect members status via ref statefulset
 	sts := &appv1.StatefulSet{}
 	exist, err = r.Exist(stsRef, sts)
 	if err != nil {
diff --git a/internal/reconciler/reconcile_test.go b/internal/reconciler/reconcile_test.go
new file mode 100644
index 00000000..8f2a6d23
--- /dev/null
+++ b/internal/reconciler/reconcile_test.go
@@ -0,0 +1,29 @@
+package reconciler
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	xapi "github.com/xline-kv/xline-operator/api/v1alpha1"
+)
+
+func TestStatusConvert(t *testing.T) {
+	sucStage := clusterStageSucc(xapi.StageXlineService)
+	failStage := clusterStageFail(xapi.StageXlineService, errors.New("failed to create service"))
+
+	t.Run("Successful Status can covert to XlineClusterRecStatus properly", func(t *testing.T) {
+		res := sucStage.AsXlineClusterRecStatus()
+		assert.Equal(t, res.Stage, xapi.StageXlineService)
+		assert.Equal(t, res.StageStatus, xapi.StageResultSucceeded)
+		assert.True(t, res.LastMessage == "")
+	})
+
+	t.Run("Failed Status can covert to XlineClusterRecStatus properly", func(t *testing.T) {
+		res := failStage.AsXlineClusterRecStatus()
+		assert.Equal(t, res.Stage, xapi.StageXlineService)
+		assert.Equal(t, res.StageStatus, xapi.StageResultFailed)
+		assert.Equal(t, res.LastMessage, "failed to create service")
+	})
+
+}
diff --git a/internal/transformer/xlinecluster_resource_test.go b/internal/transformer/xlinecluster_resource_test.go
new file mode 100644
index 00000000..b007c03f
--- /dev/null
+++ b/internal/transformer/xlinecluster_resource_test.go
@@ -0,0 +1,59 @@
+package transformer
+
+import (
+	"fmt"
+	"strings"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	xapi "github.com/xline-kv/xline-operator/api/v1alpha1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func TestXlineClusterFunc(t *testing.T) {
+	test_image := "xline-img"
+	test_image_version := "latest"
+	xlineCluster := xapi.XlineCluster{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "xline",
+			Namespace: "default",
+		},
+		Spec: xapi.XlineClusterSpec{
+			Version:  test_image_version,
+			Image:    &test_image,
+			Replicas: 3,
+		},
+	}
+
+	t.Run("GetServiceKey should work properly", func(t *testing.T) {
+		xcLookupKey := xlineCluster.ObjKey()
+		svcObj := GetServiceKey(xcLookupKey)
+		assert.Equal(t, svcObj.Namespace, "default")
+		assert.Equal(t, svcObj.Name, "xline-svc")
+	})
+
+	t.Run("GetStatefulSetKey should work properly", func(t *testing.T) {
+		xcLookupKey := xlineCluster.ObjKey()
+		stsObj := GetStatefulSetKey(xcLookupKey)
+		assert.Equal(t, stsObj.Namespace, "default")
+		assert.Equal(t, stsObj.Name, "xline-sts")
+	})
+
+	t.Run("GetXlineImage should work properly", func(t *testing.T) {
+		xline_image := GetXlineImage(&xlineCluster)
+		assert.Equal(t, xline_image, "xline-img:latest")
+	})
+
+	t.Run("GetMemberTopology should work properly", func(t *testing.T) {
+		xcLookupKey := xlineCluster.ObjKey()
+		stsRef := GetStatefulSetKey(xcLookupKey)
+		svcName := GetServiceKey(xcLookupKey).Name
+		topology := GetMemberTopology(stsRef, svcName, 3)
+		topologyVec := strings.Split(topology, ",")
+		assert.Equal(t, len(topologyVec), 3)
+		for i := 0; i < 3; i++ {
+			expectRes := fmt.Sprintf("xline-sts-%d=xline-sts-%d.xline-svc.default.svc.cluster.local:2379", i, i)
+			assert.Equal(t, topologyVec[i], expectRes)
+		}
+	})
+}
diff --git a/internal/util/kubeutil_test.go b/internal/util/kubeutil_test.go
new file mode 100644
index 00000000..1b076540
--- /dev/null
+++ b/internal/util/kubeutil_test.go
@@ -0,0 +1,13 @@
+package util
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"k8s.io/apimachinery/pkg/types"
+)
+
+func TestK8sObjKeyStr(t *testing.T) {
+	objkey := types.NamespacedName{Name: "xline", Namespace: "default"}
+	assert.Equal(t, K8sObjKeyStr(objkey), "xline.default")
+}