4

I am trying to mock Stripe for some tests.

//testify mock
type Backend struct {
    mock.Mock
}

func (s Backend) Call(method, path, key string, params stripe.ParamsContainer, v interface{}) error {
    args := s.Called(params)
    return args.Error(0)
}

func (s Backend) CallRaw(method, path, key string, body *form.Values, params *stripe.Params, v interface{}) error {
    args := s.Called(params)
    return args.Error(0)
}

func (s Backend) CallMultipart(method, path, key, boundary string, body *bytes.Buffer, params *stripe.Params, v interface{}) error {
    args := s.Called(params)
    return args.Error(0)
}

func (s Backend) SetMaxNetworkRetries(maxNetworkRetries int) {
    s.Called(maxNetworkRetries)
}

then in test initialise:

//stripe
backend := new(mock.Backend)
backend.On("CallRaw", testify.Anything, testify.Anything, testify.Anything, testify.Anything, testify.Anything).
    Return(nil)
backend.On("Call",
    testify.
        MatchedBy(func(req stripe.ParamsContainer) bool {
            customerParams, ok := req.(*stripe.CustomerParams)
            if ok {
                return *customerParams.Email == "failure@email.com"
            }
            return false
        })).
    Return(fmt.Errorf("downstream stripe error"))
backend.On("Call",
    testify.
        MatchedBy(func(req stripe.ParamsContainer) bool {
            customerParams, ok := req.(*stripe.CustomerParams)
            if ok {
                return *customerParams.Email == "success@email.com"
            }
            return false
        })).
    Return(nil)
sc := &client.API{}
sc.Init("", &stripe.Backends{
    API:     backend,
    Connect: backend,
    Uploads: backend,
})

This works - but I can't work out how I can mock to actually get the Customer? I don't want to mock client.API. The API code: https://github.com/stripe/stripe-go/blob/9c5fd87e31fd4a072b4d92571d67437e329dc9db/customer/client.go#L23

Is anyone else doing this? :)

Thanks

mryan
  • 382
  • 1
  • 4
  • 18
  • Are you aware of https://github.com/stripe/stripe-mock ? – marco.m Jun 23 '20 at 14:09
  • I am - but this was more for unit tests - I actually just implemented a thin wrapper around Stripe methods...I'm fine with this approach tbh. For functional tests will use stripe-mock. – mryan Jun 25 '20 at 15:32

2 Answers2

1

Here the solution for stripe-go v72 (partially inspired by @kozmo answer)

import (
    "bytes"

    "github.com/stretchr/testify/mock"
    "github.com/stripe/stripe-go/v72"
    "github.com/stripe/stripe-go/v72/form"
)

type stripeBackendMock struct {
    mock.Mock
}

func (s *stripeBackendMock) Call(method, path, key string, params stripe.ParamsContainer, v stripe.LastResponseSetter) error {
    args := s.Called(method, path, key, params, v)
    return args.Error(0)
}

func (s *stripeBackendMock) CallStreaming(method, path, key string, params stripe.ParamsContainer, v stripe.StreamingLastResponseSetter) error {
    args := s.Called(method, path, key, params, v)
    return args.Error(0)
}

func (s *stripeBackendMock) CallRaw(method, path, key string, body *form.Values, params *stripe.Params, v stripe.LastResponseSetter) error {
    args := s.Called(method, path, key, params, v)
    return args.Error(0)
}

func (s *stripeBackendMock) CallMultipart(method, path, key, boundary string, body *bytes.Buffer, params *stripe.Params, v stripe.LastResponseSetter) error {
    args := s.Called(method, path, key, boundary, body, params, v)
    return args.Error(0)
}

func (s *stripeBackendMock) SetMaxNetworkRetries(maxNetworkRetries int64) {
    s.Called(maxNetworkRetries)
}

Then in your tests

stripeBackendMock := new(stripeBackendMock)    
stripeTestBackends := &stripe.Backends{
    API: stripeBackendMock,
    Connect: stripeBackendMock,
    Uploads: stripeBackendMock,
}

stripe = client.New("sk_test", stripeTestBackends)
// ...

stripeBackendMock.On("Call", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
    stripeCustomer := args.Get(4).(*stripe.Customer)
    *stripeCustomer = stripe.Customer{ /* mock here */ }
}).Return(nil).Once()
// check stripe.Customers.New(...)
saniales
  • 451
  • 3
  • 14
0

I think you incorrect implement the interface.

My realization for github.com/stripe/stripe-go/v71 v71.27.0:

import (
    "bytes"
    "github.com/stretchr/testify/mock"
    "github.com/stripe/stripe-go/v71"
    "github.com/stripe/stripe-go/v71/form"
)

type Backend struct {
    mock.Mock
}

func (s *Backend) Call(method, path, key string, params stripe.ParamsContainer, v stripe.LastResponseSetter) error {
    args := s.Called(method, path, key, params, v)
    return args.Error(0)
}

func (s *Backend) CallRaw(method, path, key string, body *form.Values, params *stripe.Params, v stripe.LastResponseSetter) error {
    args := s.Called(method, path, key, body, params, v)
    return args.Error(0)
}

func (s *Backend) CallMultipart(method, path, key, boundary string, body *bytes.Buffer, params *stripe.Params, v stripe.LastResponseSetter) error {
    args := s.Called(method, path, key, boundary, body, params, v)
    return args.Error(0)
}

func (s *Backend) SetMaxNetworkRetries(maxNetworkRetries int64) {
    s.Called(maxNetworkRetries)
}

To implement mock you have to use a pointer as target func (s *Backend) SetMaxNetworkRetries(maxNetworkRetries int64){...} instead of a copy target (func (s Backend) SetMaxNetworkRetries(maxNetworkRetries int64){...}) to a method.

Аlso you have to strictly follow a method signature.

kozmo
  • 4,024
  • 3
  • 30
  • 48