英文:
Conditional Mocking in Go
问题
我想根据特定条件在GoLang中模拟Rest API调用。
例如,我有一个如下所示的方法:
func Method() {
resp1, err := DoSomething1()
resp2, err := DoSomething2()
}
func DoSomething1() (*http.Response, error) {
return Get(url string)
}
func DoSomething2() (*http.Response, error) {
return Get(url string)
}
现在,我想为每个get调用单独定义模拟响应。也就是说,当我从Method()调用DoSomething1()时,我想模拟get调用并返回响应R1,当我从Method()调用DoSomething2()时,我想模拟get调用并返回响应R2。
到目前为止,我已经能够模拟单个get调用,该调用将为任何请求返回相同的响应,方法是将Get定义为接口并进行模拟。但是,我正在尝试为这些调用定义响应。
下面是我定义的用于模拟get/post调用的模拟接口:
package clientMock
import (
"net/http"
)
type MockClient struct {
}
var (
GetMock func(url string, queryParams map[string]interface{}, headerParams map[string]interface{}) (*http.Response, error)
PostMock func(url string, reqBody interface{}, queryParams map[string]interface{}, headerParams map[string]interface{}) (*http.Response, error)
)
func (m MockClient) Get(url string, queryParams map[string]interface{}, headerParams map[string]interface{}) (*http.Response, error) {
return GetMock(url, queryParams, headerParams)
}
func (m MockClient) Post(url string, reqBody interface{}, queryParams map[string]interface{}, headerParams map[string]interface{}) (*http.Response, error) {
return PostMock(url, reqBody, queryParams, headerParams)
}
下面是我为测试Method定义的示例测试类,但是我无法弄清楚如何在Method()中调用DoSomething2()时更改模拟响应:
func TestHandleSuccess(t *testing.T) {
resBody := `{"instance_url":"instance_url","access_token":"access_token"}`
r := ioutil.NopCloser(bytes.NewReader([]byte(resBody)))
clientMock.GetMock = func(url string, queryParams, headerParams map[string]interface{}) (*http.Response, error) {
return &http.Response{
StatusCode: 200,
Body: r,
}, nil
}
err := method.Method()
assert.Nil(t, err)
}
英文:
I want to mock Rest API calls in GoLang based on certain condition.
For example, i have a method defined like below :
func Method() {
resp1, err := DoSomething1()
resp2, err := DoSomething2()
}
func DoSomething1() (*http.Response, error) {
return Get(url string)
}
func DoSomething2() (*http.Response, error) {
return Get(url string)
}
Now, i want to define my mock response for each get calls separately. i.e when i call DoSomething1() from Method(), i want to mock the get call and return response R1 and when i call DoSomething2() from Method(), i want to mock the get call and return response R2.
So far, i have been able to mock a single get call that would return same response for any request by defining Get as an interface and mocking it, however i am trying to define responses for each of these calls.
Below is the mock interface that i have defined that i am using to mock get/post calls :
package clientMock
import (
"net/http"
)
type MockClient struct {
}
var (
GetMock func(url string, queryParams map[string]interface{}, headerParams map[string]interface{}) (*http.Response, error)
PostMock func(url string, reqBody interface{}, queryParams map[string]interface{}, headerParams map[string]interface{}) (*http.Response, error)
)
func (m MockClient) Get(url string, queryParams map[string]interface{}, headerParams map[string]interface{}) (*http.Response, error) {
return GetMock(url, queryParams, headerParams)
}
func (m MockClient) Post(url string, reqBody interface{}, queryParams map[string]interface{}, headerParams map[string]interface{}) (*http.Response, error) {
return PostMock(url, reqBody, queryParams, headerParams)
}
Below is the sample Test Class that i have defined for testing Method, however unable to figure out how can i change the mock response when DoSomething2() is being called from Method() :
func TestHandleSuccess(t *testing.T) {
resBody := `{"instance_url":"instance_url","access_token":"access_token"}`
r := ioutil.NopCloser(bytes.NewReader([]byte(resBody)))
clientMock.GetMock = func(url string, queryParams, headerParams map[string]interface{}) (*http.Response, error) {
return &http.Response{
StatusCode: 200,
Body: r,
}, nil
}
err := method.Method()
assert.Nil(t, err)
}
答案1
得分: 0
根据你提供的代码,我猜测从你的“做某事”函数传递给Get
的参数url
是不同的(否则我不确定为什么你需要改变行为)。如果是这样的话,你可以在模拟函数中添加一个条件,根据url
来改变行为。
以下是代码示例:
clientMock.GetMock = func(url string, queryParams, headerParams map[string]interface{}) (*http.Response, error) {
var resBody string
if url == "/call1" {
resBody = `{"instance_url":"instance_url","access_token":"access_token"}`
} else if url == "/call2" {
resBody = `{"something":"else"}`
}
return &http.Response{
StatusCode: 200,
Body: ioutil.NopCloser(bytes.NewReader([]byte(resBody))),
}, nil
}
你应该这样思考你的代码 - 行为不应该根据调用的位置而改变,而应该根据调用函数的输入来改变。这样可以将函数与其调用者解耦,使重用和重构更容易。
英文:
Given the code you've provided, I'm guessing the parameter url
passed into Get
from your "do something" functions is different (otherwise I'm not sure why you'd need the behavior to change). If this is the case, you could add a condition in your mock to change behavior based on the url
.
clientMock.GetMock = func(url string, queryParams, headerParams map[string]interface{}) (*http.Response, error) {
var resBody string
if url == "/call1" {
resBody = `{"instance_url":"instance_url","access_token":"access_token"}`
} else if url == "/call2" {
resBody = `{"something":"else"}`
}
return &http.Response{
StatusCode: 200,
Body: ioutil.NopCloser(bytes.NewReader([]byte(resBody))),
}, nil
}
This is how you should think about your code - behavior shouldn't change based on where a call is coming from. Instead, it should change based on the inputs to the function being called. This decouples your function from its caller and makes reuse and refactoring easier.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论