如何模拟在 Go 中将结果写入其参数的函数

我正在通过https://github.com/stretchr/testify在 golang 中编写单元测试 假设我有以下方法,


func DoSomething(result interface{}) error {

    // write some data to result

    return nil

}

所以调用者可以调用DoSomething如下


result := &SomeStruct{}

err := DoSomething(result)


if err != nil {

  fmt.Println(err)

} else {

  fmt.Println("The result is", result)

}

现在我知道如何使用testify或其他一些模拟工具来模拟返回值(它在err这里),比如


mockObj.On("DoSomething", mock.Anything).Return(errors.New("mock error"))

result我的问题是在这种情况下“我如何模拟论点”?


由于result不是返回值而是参数,调用者通过传递一个结构的指针来调用它,函数对其进行修改。


慕码人8056858
浏览 133回答 2
2回答

慕尼黑8549860

您可以使用以下(*Call).Run方法:Run 设置在返回之前调用的处理程序。它可以在模拟一个方法(例如解组器)时使用,该方法接受一个指向结构的指针并在该结构中设置属性例子:mockObj.On("Unmarshal", mock.AnythingOfType("*map[string]interface{}")).Return().Run(func(args Arguments) {    arg := args.Get(0).(*map[string]interface{})    arg["foo"] = "bar"})

鸿蒙传说

services/message.go:type messageService struct {&nbsp; &nbsp; HttpClient http.Client&nbsp; &nbsp; BaseURL&nbsp; &nbsp; string}func (m *messageService) MarkAllMessages(accesstoken string) []*model.MarkedMessage {&nbsp; &nbsp; endpoint := m.BaseURL + "/message/mark_all"&nbsp; &nbsp; var res model.MarkAllMessagesResponse&nbsp; &nbsp; if err := m.HttpClient.Post(endpoint, &MarkAllMessagesRequestPayload{Accesstoken: accesstoken}, &res); err != nil {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(err)&nbsp; &nbsp; &nbsp; &nbsp; return res.MarkedMsgs&nbsp; &nbsp; }&nbsp; &nbsp; return res.MarkedMsgs}我们传递res给m.HttpClient.Post方法。在此方法中,res将填充json.unmarshal方法。mocks/http.go:package mocksimport (&nbsp; &nbsp; "io"&nbsp; &nbsp; "github.com/stretchr/testify/mock")type MockedHttp struct {&nbsp; &nbsp; mock.Mock}func (m *MockedHttp) Get(url string, data interface{}) error {&nbsp; &nbsp; args := m.Called(url, data)&nbsp; &nbsp; return args.Error(0)}func (m *MockedHttp) Post(url string, body interface{}, data interface{}) error {&nbsp; &nbsp; args := m.Called(url, body, data)&nbsp; &nbsp; return args.Error(0)}services/message_test.go:package services_testimport (&nbsp; &nbsp; "errors"&nbsp; &nbsp; "reflect"&nbsp; &nbsp; "strconv"&nbsp; &nbsp; "testing"&nbsp; &nbsp; "github.com/stretchr/testify/mock"&nbsp; &nbsp; "github.com/mrdulin/gqlgen-cnode/graph/model"&nbsp; &nbsp; "github.com/mrdulin/gqlgen-cnode/services"&nbsp; &nbsp; "github.com/mrdulin/gqlgen-cnode/mocks")const (&nbsp; &nbsp; baseURL&nbsp; &nbsp; &nbsp;string = "http://localhost/api/v1"&nbsp; &nbsp; accesstoken string = "123")func TestMessageService_MarkAllMessages(t *testing.T) {&nbsp; &nbsp; t.Run("should mark all messaages", func(t *testing.T) {&nbsp; &nbsp; &nbsp; &nbsp; testHttp := new(mocks.MockedHttp)&nbsp; &nbsp; &nbsp; &nbsp; var res model.MarkAllMessagesResponse&nbsp; &nbsp; &nbsp; &nbsp; var markedMsgs []*model.MarkedMessage&nbsp; &nbsp; &nbsp; &nbsp; for i := 1; i <= 3; i++ {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; markedMsgs = append(markedMsgs, &model.MarkedMessage{ID: strconv.Itoa(i)})&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; postBody := services.MarkAllMessagesRequestPayload{Accesstoken: accesstoken}&nbsp; &nbsp; &nbsp; &nbsp; testHttp.On("Post", baseURL+"/message/mark_all", &postBody, &res).Return(nil).Run(func(args mock.Arguments) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; arg := args.Get(2).(*model.MarkAllMessagesResponse)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; arg.MarkedMsgs = markedMsgs&nbsp; &nbsp; &nbsp; &nbsp; })&nbsp; &nbsp; &nbsp; &nbsp; service := services.NewMessageService(testHttp, baseURL)&nbsp; &nbsp; &nbsp; &nbsp; got := service.MarkAllMessages(accesstoken)&nbsp; &nbsp; &nbsp; &nbsp; want := markedMsgs&nbsp; &nbsp; &nbsp; &nbsp; testHttp.AssertExpectations(t)&nbsp; &nbsp; &nbsp; &nbsp; if !reflect.DeepEqual(got, want) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t.Errorf("got wrong return value. got: %#v, want: %#v", got, want)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; })&nbsp; &nbsp; t.Run("should print error and return empty slice", func(t *testing.T) {&nbsp; &nbsp; &nbsp; &nbsp; var res model.MarkAllMessagesResponse&nbsp; &nbsp; &nbsp; &nbsp; testHttp := new(mocks.MockedHttp)&nbsp; &nbsp; &nbsp; &nbsp; postBody := services.MarkAllMessagesRequestPayload{Accesstoken: accesstoken}&nbsp; &nbsp; &nbsp; &nbsp; testHttp.On("Post", baseURL+"/message/mark_all", &postBody, &res).Return(errors.New("network"))&nbsp; &nbsp; &nbsp; &nbsp; service := services.NewMessageService(testHttp, baseURL)&nbsp; &nbsp; &nbsp; &nbsp; got := service.MarkAllMessages(accesstoken)&nbsp; &nbsp; &nbsp; &nbsp; var want []*model.MarkedMessage&nbsp; &nbsp; &nbsp; &nbsp; testHttp.AssertExpectations(t)&nbsp; &nbsp; &nbsp; &nbsp; if !reflect.DeepEqual(got, want) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t.Errorf("got wrong return value. got: %#v, want: %#v", got, want)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; })}在单元测试用例中,我们填充了res# Call.Run方法并将返回值(res.MarkedMsgs)分配service.MarkAllMessages(accesstoken)给got变量。单元测试结果和覆盖率:=== RUN&nbsp; &nbsp;TestMessageService_MarkAllMessages--- PASS: TestMessageService_MarkAllMessages (0.00s)=== RUN&nbsp; &nbsp;TestMessageService_MarkAllMessages/should_mark_all_messaages&nbsp; &nbsp; TestMessageService_MarkAllMessages/should_mark_all_messaages: message_test.go:39: PASS: Post(string,*services.MarkAllMessagesRequestPayload,*model.MarkAllMessagesResponse)&nbsp; &nbsp; --- PASS: TestMessageService_MarkAllMessages/should_mark_all_messaages (0.00s)=== RUN&nbsp; &nbsp;TestMessageService_MarkAllMessages/should_print_error_and_return_empty_slicenetwork&nbsp; &nbsp; TestMessageService_MarkAllMessages/should_print_error_and_return_empty_slice: message_test.go:53: PASS: Post(string,*services.MarkAllMessagesRequestPayload,*model.MarkAllMessagesResponse)&nbsp; &nbsp; --- PASS: TestMessageService_MarkAllMessages/should_print_error_and_return_empty_slice (0.00s)PASScoverage: 5.6% of statements in ../../gqlgen-cnode/...Process finished with exit code 0
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go