日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

使用?gomonkey?Mock?函數及方法示例詳解_Golang

作者:CG國斌 ? 更新時間: 2022-07-30 編程語言

前言

在 Golang 語言中,寫單元測試的時候,不可避免的會涉及到對其他函數及方法的 Mock,即在假設其他函數及方法響應預期結果的同時,校驗被測函數的響應是否符合預期。

其中,在 Mock 其他函數及方法的時候,我們常用到的一個測試類庫是「gomonkey」。特別地,對于方法和函數的 Mock,略有差異,在這里我們就分別給出函數和方法 Mock 示例,方便大家參考。

函數

在 Golang 語言中,函數是沒有接受者的方法,其形式為

func function_name([parameter list]) [return_types] {
   函數體
}

對于函數的 Mock 相對來說比較簡單,假設我們對 A 函數進行單元測試,且 A 函數里面又調用了 B 函數,例如

func A(ctx context.Context, str string) error {
   if len(str) == 0 {
	  return errors.New("str is empty")
   }
   return test_package_name.B(ctx, str)
}

為了將 A 函數的每一行代碼都覆蓋到,則其單元測試可以寫為:

func TestA(t *testing.T) {
	type args struct {
		ctx    context.Context
		str    string
	}
	tests := []struct {
		name    string
		args    args
		Setup   func(t *testing.T)
		wantErr error
	}{
		{
			name: "len(str) == 0",
			wantErr: errors.New("str is empty")
		},
		{
			name: "正常響應",
			Setup: func(t *testing.T) {
				patches := gomonkey.ApplyFunc(test_package_name.B, func(_ context.Context, _ string) error {
					return nil
				})
				t.Cleanup(func() {
					patches.Reset()
				})
			},
			args: args{
				ctx:     context.Background(),
				str:     "test",
			},
			wantErr: nil,
		},
	}

	// 執行測試用例
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if tt.Setup != nil {
				tt.Setup(t)
			}
			err := A(tt.args.ctx, tt.args.str)
			if err != nil {
				assert.EqualError(t, err, tt.wantErr.Error(), "error 不符合預期")
			}
		})
	}
}

其中,ApplyFunc函數是用來 Mock 函數的,其第一個參數為需要 Mock 的函數名稱(不需要寫參數列表),第二個參數為需要 Mock 的函數結果;特別地,在Setup里面,我們要記得顯式調用Cleanuppatches進行Reset操作,防止該 Mock 影響其他測試用例。

方法

在 Golang 語言中,方法是含有接受者的函數,其形式為

func (variable_name variable_data_type) function_name([parameter list]) [return_type]{
   函數體
}

對于方法的 Mock 相對來說復雜一下,假設我們對 A 函數進行單元測試,且 A 函數里面又調用了結構 C 的 B 方法,例如

func A(ctx context.Context, str string) error {
   if len(str) == 0 {
	  return errors.New("str is empty")
   }
   c := &test_package_name.C{}
   return c.B(ctx, str)
}

為了將 A 函數的每一行代碼都覆蓋到,則其單元測試可以寫為:

func TestA(t *testing.T) {
	// 初始化C結構
	var c *test_package_name.C
	
	type args struct {
		ctx    context.Context
		str    string
	}
	tests := []struct {
		name    string
		args    args
		Setup   func(t *testing.T)
		wantErr error
	}{
		{
			name: "len(str) == 0",
			wantErr: errors.New("str is empty")
		},
		{
			name: "正常響應",
			Setup: func(t *testing.T) {
				patches := gomonkey.ApplyMethod(reflect.TypeOf(c), "B", func(_ *test_package_name.C, _ context.Context, _ string) error {
					return nil
				})
				t.Cleanup(func() {
					patches.Reset()
				})
			},
			args: args{
				ctx:     context.Background(),
				str:     "test",
			},
			wantErr: nil,
		},
	}

	// 執行測試用例
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if tt.Setup != nil {
				tt.Setup(t)
			}
			err := A(tt.args.ctx, tt.args.str)
			if err != nil {
				assert.EqualError(t, err, tt.wantErr.Error(), "error 不符合預期")
			}
		})
	}
}

其中,ApplyMethod函數是用來 Mock 方法的,其第一個參數為需要 Mock 的方法的接受者類型,第二個參數為需要 Mock 的方法名稱(字符串類型),第三個參數為需要 Mock 的方法的定義及 Mock 結果;特別地,第一個參數和第三個參數需要我們注意:

  • 第一個參數,需要使用reflect.TypeOf獲取接受者的類型,初始化的接受者必須是真正的類型,如結構 C 組合了結構 D,而B方法是通過組合 D 得到的,則初始化的時候需要定義結構 D,而不是結構 C,否則會報空指針異常;
  • 第三個參數,雖然B方法的聲明是func(ctx context.Context, str string),但是在使用ApplyMethod的時候,需要將B方法的聲明修改為func(c *test_package_name.C, ctx context.Context, str string),即需要將方法的接受者置為方法的第一個參數。

參考

還有就是,大家在使用gomonkey的時候,有可能遇到權限校驗的問題以及非 Debug 模式運行失敗的問題,可以參考:

golang使用gomonkey和monkey來mock方法或者函數時報panic: permission denied

使用 gomonkey 遇到非 debug 模式執行失敗的問題及解決方法

到這里,本文就要結束了,希望對大家有所幫助。

原文鏈接:https://blog.csdn.net/qq_35246620/article/details/125096233

欄目分類
最近更新