Repository: knative/client (go)

Issue:
[tests] Add mock test client for dynamic client
Add mock test client for dynamic client to streamline unit testing.
We should be able to pass the errors to expect while executing via dynamic client.
Ref implementation for mock client: https://github.com/knative/client/blob/master/pkg/sources/v1alpha2/apiserver_client_mock.go#L26

Pull request:
Add mock tests for the dynamic client
## Description

Adding mock test client for the dynamic client.

## Changes
Added mock tests for Dynamic client.

Fixes #952

Patch:
diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc
index 21a8c2d2..c4548557 100644
--- a/CHANGELOG.adoc
+++ b/CHANGELOG.adoc
@@ -38,6 +38,9 @@
 | Fix exit code for `kn service delete` and `kn revision delete` failures
 | https://github.com/knative/client/pull/971[#971]
 
+| 🎁
+| Add mock test client for dynamic client
+| https://github.com/knative/client/pull/972[#972]
 |===
 
 ## v0.16.0 (2020-07-14)
diff --git a/pkg/dynamic/client_mock.go b/pkg/dynamic/client_mock.go
new file mode 100644
index 00000000..b20a0544
--- /dev/null
+++ b/pkg/dynamic/client_mock.go
@@ -0,0 +1,122 @@
+// Copyright © 2019 The Knative Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dynamic
+
+import (
+	"testing"
+
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/client-go/dynamic"
+	"knative.dev/client/pkg/util/mock"
+)
+
+// MockKnDynamicClient is a combine of test object and recorder
+type MockKnDynamicClient struct {
+	t         *testing.T
+	recorder  *ClientRecorder
+	namespace string
+}
+
+// NewMockKnDyanmicClient returns a new mock instance which you need to record for
+func NewMockKnDyanmicClient(t *testing.T, ns ...string) *MockKnDynamicClient {
+	namespace := "default"
+	if len(ns) > 0 {
+		namespace = ns[0]
+	}
+	return &MockKnDynamicClient{
+		t:        t,
+		recorder: &ClientRecorder{mock.NewRecorder(t, namespace)},
+	}
+}
+
+// Ensure that the interface is implemented
+var _ KnDynamicClient = &MockKnDynamicClient{}
+
+// ClientRecorder is recorder for eventing objects
+type ClientRecorder struct {
+	r *mock.Recorder
+}
+
+// Recorder returns the recorder for registering API calls
+func (c *MockKnDynamicClient) Recorder() *ClientRecorder {
+	return c.recorder
+}
+
+// Namespace of this client
+func (c *MockKnDynamicClient) Namespace() string {
+	return c.recorder.r.Namespace()
+}
+
+// ListCRDs returns list of installed CRDs in the cluster and filters based on the given options
+func (dr *ClientRecorder) ListCRDs(options interface{}, ulist *unstructured.UnstructuredList, err error) {
+	dr.r.Add("ListCRDs", []interface{}{options}, []interface{}{ulist, err})
+}
+
+// ListCRDs returns list of installed CRDs in the cluster and filters based on the given options
+func (c *MockKnDynamicClient) ListCRDs(options metav1.ListOptions) (*unstructured.UnstructuredList, error) {
+	call := c.recorder.r.VerifyCall("ListCRDs", options)
+	return call.Result[0].(*unstructured.UnstructuredList), mock.ErrorOrNil(call.Result[1])
+}
+
+// ListSourcesTypes returns installed knative eventing sources CRDs
+func (dr *ClientRecorder) ListSourcesTypes(ulist *unstructured.UnstructuredList, err error) {
+	dr.r.Add("ListSourcesTypes", []interface{}{}, []interface{}{ulist, err})
+}
+
+// ListSourcesTypes returns installed knative eventing sources CRDs
+func (c *MockKnDynamicClient) ListSourcesTypes() (*unstructured.UnstructuredList, error) {
+	call := c.recorder.r.VerifyCall("ListSourcesTypes")
+	return call.Result[0].(*unstructured.UnstructuredList), mock.ErrorOrNil(call.Result[1])
+}
+
+// ListSources returns list of available sources objects
+func (dr *ClientRecorder) ListSources(types interface{}, ulist *unstructured.UnstructuredList, err error) {
+	dr.r.Add("ListSources", []interface{}{types}, []interface{}{ulist, err})
+}
+
+// ListSources returns list of available sources objects
+func (c *MockKnDynamicClient) ListSources(types ...WithType) (*unstructured.UnstructuredList, error) {
+	call := c.recorder.r.VerifyCall("ListSources")
+	return call.Result[0].(*unstructured.UnstructuredList), mock.ErrorOrNil(call.Result[1])
+}
+
+// RawClient creates a client
+func (dr *ClientRecorder) RawClient(dynamicInterface dynamic.Interface) {
+	dr.r.Add("RawClient", []interface{}{}, []interface{}{dynamicInterface})
+}
+
+// RawClient creates a client
+func (c *MockKnDynamicClient) RawClient() (dynamicInterface dynamic.Interface) {
+	call := c.recorder.r.VerifyCall("RawClient")
+	return call.Result[0].(dynamic.Interface)
+}
+
+// ListSourcesUsingGVKs returns list of available source objects using given list of GVKs
+func (dr *ClientRecorder) ListSourcesUsingGVKs(gvks interface{}, types interface{}, ulist *unstructured.UnstructuredList, err error) {
+	dr.r.Add("ListSourcesUsingGVKs", []interface{}{gvks, types}, []interface{}{ulist, err})
+}
+
+// ListSourcesUsingGVKs returns list of available source objects using given list of GVKs
+func (c *MockKnDynamicClient) ListSourcesUsingGVKs(gvks *[]schema.GroupVersionKind, types ...WithType) (*unstructured.UnstructuredList, error) {
+	call := c.recorder.r.VerifyCall("ListSourcesUsingGVKs")
+	return call.Result[0].(*unstructured.UnstructuredList), mock.ErrorOrNil(call.Result[1])
+}
+
+// Validate validates whether every recorded action has been called
+func (dr *ClientRecorder) Validate() {
+	dr.r.CheckThatAllRecordedMethodsHaveBeenCalled()
+}


Test patch:
diff --git a/pkg/dynamic/client_mock_test.go b/pkg/dynamic/client_mock_test.go
new file mode 100644
index 00000000..38ca3f74
--- /dev/null
+++ b/pkg/dynamic/client_mock_test.go
@@ -0,0 +1,45 @@
+// Copyright © 2019 The Knative Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dynamic
+
+import (
+	"testing"
+
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/client-go/dynamic/fake"
+	"knative.dev/client/pkg/util/mock"
+)
+
+func TestMockKnDynamicClient(t *testing.T) {
+
+	client := NewMockKnDyanmicClient(t)
+
+	recorder := client.Recorder()
+
+	recorder.ListCRDs(mock.Any(), nil, nil)
+	recorder.ListSourcesTypes(nil, nil)
+	recorder.ListSources(mock.Any(), nil, nil)
+	recorder.RawClient(&fake.FakeDynamicClient{})
+	recorder.ListSourcesUsingGVKs(mock.Any(), mock.Any(), nil, nil)
+
+	client.ListCRDs(metav1.ListOptions{})
+	client.ListSourcesTypes()
+	client.ListSources(WithTypeFilter("blub"))
+	client.RawClient()
+	client.ListSourcesUsingGVKs(&[]schema.GroupVersionKind{}, WithTypeFilter("blub"))
+	// Validate
+	recorder.Validate()
+}