如何在Stripe的PaymentIntent中Mock内部HTTP客户端?
解决Stripe paymentintent.New()单元测试Mock HTTP客户端的问题
直接使用paymentintent.New()顶层函数时,因为它内部会实例化默认的Stripe客户端,确实没法直接注入自定义的HTTP客户端做Mock。这里提供三种可行的解决方案:
方案1:重构代码,依赖注入paymentintent.Client(推荐)
Stripe Go SDK支持创建自定义的paymentintent.Client实例,它允许你传入自己配置的stripe.Client(包含自定义HTTP客户端)。通过依赖注入客户端实例,就能完全控制测试时的HTTP请求行为。
重构生产代码
把原来直接调用paymentintent.New()的逻辑,改为接收paymentintent.Client参数:
import ( "github.com/stripe/stripe-go/v79" "github.com/stripe/stripe-go/v79/paymentintent" ) // 重构后的函数,依赖注入客户端 func CreatePaymentIntent(client *paymentintent.Client, amount int64) (*stripe.PaymentIntent, error) { params := &stripe.PaymentIntentParams{ Amount: stripe.Int64(amount), Currency: stripe.String(string(stripe.CurrencyUSD)), } return client.New(params) } // 业务代码中使用默认客户端 func main() { stripe.Key = "your-stripe-secret-key" defaultPiClient := paymentintent.NewClient(stripe.DefaultClient) pi, err := CreatePaymentIntent(defaultPiClient, 1000) // 处理结果... }
编写单元测试(用httptest模拟Stripe API)
用net/http/httptest搭建本地测试服务器,模拟Stripe的响应:
import ( "net/http" "net/http/httptest" "testing" "github.com/stripe/stripe-go/v79" "github.com/stripe/stripe-go/v79/paymentintent" "github.com/stretchr/testify/assert" ) func TestCreatePaymentIntent(t *testing.T) { // 启动测试服务器,模拟Stripe API响应 testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // 验证请求的合法性 assert.Equal(t, http.MethodPost, r.Method) assert.Equal(t, "/v1/payment_intents", r.URL.Path) // 返回模拟的Payment Intent数据 w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write([]byte(`{ "id": "pi_test_123", "amount": 1000, "currency": "usd", "status": "requires_payment_method" }`)) })) defer testServer.Close() // 创建自定义Stripe客户端,指向测试服务器 customStripeClient := &stripe.Client{ Key: "test-secret-key", BaseURL: testServer.URL, HTTPClient: &http.Client{}, // 这里可以替换为mock的HTTP客户端 } // 创建paymentintent客户端 piClient := paymentintent.NewClient(customStripeClient) // 测试目标函数 pi, err := CreatePaymentIntent(piClient, 1000) // 验证结果 assert.NoError(t, err) assert.Equal(t, "pi_test_123", pi.ID) assert.Equal(t, int64(1000), pi.Amount) }
方案2:临时替换全局默认客户端(无需大规模重构)
如果不想修改太多业务代码,可以临时替换stripe.DefaultClient,但要注意测试前后的清理,避免测试间的状态污染:
func TestCreatePaymentIntentWithDefaultClient(t *testing.T) { // 备份原始默认客户端 originalDefaultClient := stripe.DefaultClient defer func() { // 测试结束后恢复原始客户端 stripe.DefaultClient = originalDefaultClient }() // 启动测试服务器 testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // 返回模拟响应 w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write([]byte(`{"id": "pi_test_456", "amount": 2000, "currency": "usd"}`)) })) defer testServer.Close() // 设置新的默认客户端 stripe.DefaultClient = &stripe.Client{ Key: "test-key", BaseURL: testServer.URL, } // 直接调用原来的CreatePaymentIntent函数(未重构版本) pi, err := CreatePaymentIntent(2000) assert.NoError(t, err) assert.Equal(t, "pi_test_456", pi.ID) }
方案3:用Mock库直接Mock HTTP客户端
如果需要更细粒度的控制,可以用testify/mock这类库直接Mockhttp.Client的Do方法:
import ( "io/ioutil" "net/http" "strings" "testing" "github.com/stripe/stripe-go/v79" "github.com/stripe/stripe-go/v79/paymentintent" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) // MockHTTPClient 实现http.Client的Do接口 type MockHTTPClient struct { mock.Mock } func (m *MockHTTPClient) Do(req *http.Request) (*http.Response, error) { args := m.Called(req) return args.Get(0).(*http.Response), args.Error(1) } func TestCreatePaymentIntentWithMockClient(t *testing.T) { // 创建Mock HTTP客户端 mockHTTPClient := new(MockHTTPClient) // 构造模拟响应 mockResp := &http.Response{ StatusCode: http.StatusOK, Header: http.Header{"Content-Type": []string{"application/json"}}, Body: ioutil.NopCloser(strings.NewReader(`{"id": "pi_mock_789", "amount": 1500}`)), } // 预期Do方法被调用,返回模拟响应 mockHTTPClient.On("Do", mock.Anything).Return(mockResp, nil) // 创建Stripe客户端,绑定Mock HTTP客户端 stripeClient := &stripe.Client{ Key: "test-key", HTTPClient: mockHTTPClient, } piClient := paymentintent.NewClient(stripeClient) pi, err := CreatePaymentIntent(piClient, 1500) assert.NoError(t, err) assert.Equal(t, "pi_mock_789", pi.ID) // 验证Mock方法被调用 mockHTTPClient.AssertExpectations(t) }
内容的提问来源于stack exchange,提问作者Ritik Jain
相关产品推荐
相关产品推荐

