模拟一个结构体字段的方法

huangapple go评论89阅读模式
英文:

Mocking a struct field's methods

问题

我正在处理一个简单的 Golang 服务器应用程序,用于进行 OAuth 认证。服务器代码很简单。在进行单元测试时,我需要避免向实际的 OAuth 提供者发出外部 HTTP 请求。

因此,在正常的服务器操作期间,我希望使用真实的调用,而在运行 go test 时,使用模拟方法来阻止外部 HTTP 请求。

我已经创建了 OAuth 方法的模拟,但我不明白如何将它们集成到代码中。通过查看模拟示例,我仍然对如何使用模拟接口感到困惑。

这是原始代码:

type Server struct {
    // 这是实际的 golang.org/x/oauth2 Config 结构体
    oauth2Config  *oauth2.Config
}


func (s *Server) handleCallback() http.HandlerFunc {
    // 这是进行外部调用的方法。我想要进行模拟
    oauth2Token, err := s.oauth2Config.Exchange(s.context, r.URL.Query().Get("code"))
}

我定义了一个接口和模拟函数:

type Oauth2ConfigInterface interface {
    Exchange(ctx context.Context, code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error)
}

type MockOauth2Config struct {
}

func (o *MockOauth2Config) Exchange(ctx context.Context, code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) {
    return &oauth2.Token{}, nil
}

我原以为应该修改原始的 Server 结构体以使用新的接口:

  • 在正常的服务器操作中,oauth2Config 字段被赋值为 &oath2.Config{}
  • 在测试中,oauth2Config 字段被赋值为 &MockOauth2Config{}
type Server struct {
    // 改为接口类型
    oauth2Config  Oauth2ConfigInterface
}

...

    oauth2Config: &oauth2.Config{
        ClientID:     "",
        ClientSecret: "",
        RedirectURL:  "",
        Scopes:       []string{oidc.ScopeOpenID},
    },

但是,当我尝试访问 oauth2Config 的字段时,它给我编译错误。

// 这里会有编译错误
s.verifier = s.provider.Verifier(&oidc.Config{ClientID: s.oauth2Config.ClientID})

一旦 oauth2Config 被声明为接口,我如何访问其字段呢?

或者,我整个模拟的方法都错了吗?

英文:

I'm working on a simple golang server app that does OAuth authentication. The server code is straight forward. For unit tests, I need to avoid making external HTTP requests to the actual OAuth provider.

So during normal Server operation, I would like to use the real calls, and when running go test, use a Mock method to prevent external HTTP requests.

I've created mocks of the OAuth methods, but I don't understand the mechanics of how to integrate them into the code. Looking through mock examples, I'm still confused about how a mock interface is used.

Here's the original code:

type Server struct {
    //This is the actual golang.org/x/oauth2 Config struct
	oauth2Config  *oauth2.Config
}


func (s *Server) handleCallback() http.HandlerFunc {
    // This is the method that makes external call. I want to mock it
	oauth2Token, err := s.oauth2Config.Exchange(s.context, r.URL.Query().Get("code"))
}

I define an interface, and the mock function

type Oauth2ConfigInterface interface {
	Exchange(ctx context.Context, code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error)
}

type MockOauth2Config struct {
}

func (o *MockOauth2Config) Exchange(ctx context.Context, code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) {
	return &oauth2.Token{}, nil
}

I would have thought I should change the original Server struct to use the new interface:

  • On normal Server operation, oauth2Config field is assigned with &oath2.Config{}
  • On tests, oauth2Config field is assigned with &MockOauth2Config{}
type Server struct {
    //Changed to interface
    oauth2Config  Oauth2ConfigInterface
}

...

		oauth2Config: &oauth2.Config{
			ClientID:     "",
			ClientSecret: "",
			RedirectURL:  "",
			Scopes:       []string{oidc.ScopeOpenID},
		},

But when I try to access the fields of oauth2Config, it gives me compile errors.

// compile error here
s.verifier = s.provider.Verifier(&oidc.Config{ClientID: s.oauth2Config.ClientID})

Once oauth2Config has been declared an interface, how can I access the fields?

Or am I doing this whole mock wrong?

答案1

得分: 1

回答我自己的问题...

我可能表达得不好,但我确实认为未来的人们会遇到同样的问题。

@bcmills 给出了最好的解决方案:
https://stackoverflow.com/questions/68940230/how-to-mock-net-interface/68941591#68941591

这种方法比使用接口进行模拟要更加优雅,特别是在涉及第三方函数/方法的情况下。

英文:

Answering my own question...

I may have phrased my question badly but I do think people in the future will run into the same question.

@bcmills gave the best solution:
https://stackoverflow.com/questions/68940230/how-to-mock-net-interface/68941591#68941591

It is far more elegant than mocking using interface. Especially for a case like this where third-party func/methods are involved.

huangapple
  • 本文由 发表于 2021年9月17日 09:52:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/69216910.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定