First stab at mock client
This commit is contained in:
parent
bfe20d81d0
commit
ac6b8b730b
16
client/mock/context.go
Normal file
16
client/mock/context.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package mock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type responseKey struct{}
|
||||||
|
|
||||||
|
func fromContext(ctx context.Context) (map[string][]MockResponse, bool) {
|
||||||
|
r, ok := ctx.Value(responseKey{}).(map[string][]MockResponse)
|
||||||
|
return r, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func newContext(ctx context.Context, r map[string][]MockResponse) context.Context {
|
||||||
|
return context.WithValue(ctx, responseKey{}, r)
|
||||||
|
}
|
174
client/mock/mock.go
Normal file
174
client/mock/mock.go
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
package mock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/micro/go-micro/client"
|
||||||
|
"github.com/micro/go-micro/errors"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ client.Client = NewClient()
|
||||||
|
)
|
||||||
|
|
||||||
|
type MockResponse struct {
|
||||||
|
Method string
|
||||||
|
Response interface{}
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
type MockClient struct {
|
||||||
|
Client client.Client
|
||||||
|
Opts client.Options
|
||||||
|
|
||||||
|
sync.Mutex
|
||||||
|
Response map[string][]MockResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockClient) Init(opts ...client.Option) error {
|
||||||
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&m.Opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
r, ok := fromContext(m.Opts.Context)
|
||||||
|
if !ok {
|
||||||
|
r = make(map[string][]MockResponse)
|
||||||
|
}
|
||||||
|
m.Response = r
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockClient) Options() client.Options {
|
||||||
|
return m.Opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockClient) NewPublication(topic string, msg interface{}) client.Publication {
|
||||||
|
return m.Client.NewPublication(topic, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockClient) NewRequest(service, method string, req interface{}, reqOpts ...client.RequestOption) client.Request {
|
||||||
|
return m.Client.NewRequest(service, method, req, reqOpts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockClient) NewProtoRequest(service, method string, req interface{}, reqOpts ...client.RequestOption) client.Request {
|
||||||
|
return m.Client.NewProtoRequest(service, method, req, reqOpts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockClient) NewJsonRequest(service, method string, req interface{}, reqOpts ...client.RequestOption) client.Request {
|
||||||
|
return m.Client.NewJsonRequest(service, method, req, reqOpts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockClient) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
|
||||||
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
|
||||||
|
response, ok := m.Response[req.Service()]
|
||||||
|
if !ok {
|
||||||
|
return errors.NotFound("go.micro.client.mock", "service not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, r := range response {
|
||||||
|
if r.Method != req.Method() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Error != nil {
|
||||||
|
return r.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
v := reflect.ValueOf(rsp)
|
||||||
|
|
||||||
|
if t := reflect.TypeOf(rsp); t.Kind() == reflect.Ptr {
|
||||||
|
v = reflect.Indirect(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Set(reflect.ValueOf(r.Response))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("rpc: can't find service %s", req.Method())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockClient) CallRemote(ctx context.Context, addr string, req client.Request, rsp interface{}, opts ...client.CallOption) error {
|
||||||
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
|
||||||
|
response, ok := m.Response[req.Service()]
|
||||||
|
if !ok {
|
||||||
|
return errors.NotFound("go.micro.client.mock", "service not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, r := range response {
|
||||||
|
if r.Method != req.Method() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Error != nil {
|
||||||
|
return r.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
v := reflect.ValueOf(rsp)
|
||||||
|
|
||||||
|
if t := reflect.TypeOf(rsp); t.Kind() == reflect.Ptr {
|
||||||
|
v = reflect.Indirect(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Set(reflect.ValueOf(r.Response))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("rpc: can't find service %s", req.Method())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockClient) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Streamer, error) {
|
||||||
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
|
||||||
|
// TODO: mock stream
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockClient) StreamRemote(ctx context.Context, addr string, req client.Request, opts ...client.CallOption) (client.Streamer, error) {
|
||||||
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
|
||||||
|
// TODO: mock stream
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockClient) Publish(ctx context.Context, p client.Publication, opts ...client.PublishOption) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockClient) String() string {
|
||||||
|
return "mock"
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(opts ...client.Option) *MockClient {
|
||||||
|
options := client.Options{
|
||||||
|
Context: context.TODO(),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
r, ok := fromContext(options.Context)
|
||||||
|
if !ok {
|
||||||
|
r = make(map[string][]MockResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &MockClient{
|
||||||
|
Client: client.DefaultClient,
|
||||||
|
Opts: options,
|
||||||
|
Response: r,
|
||||||
|
}
|
||||||
|
}
|
32
client/mock/mock_test.go
Normal file
32
client/mock/mock_test.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package mock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/micro/go-micro/errors"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestClient(t *testing.T) {
|
||||||
|
response := []MockResponse{
|
||||||
|
{Method: "Foo.Bar", Response: map[string]interface{}{"foo": "bar"}},
|
||||||
|
{Method: "Foo.Fail", Error: errors.InternalServerError("go.mock", "failed")},
|
||||||
|
}
|
||||||
|
|
||||||
|
c := NewClient(Response("go.mock", response))
|
||||||
|
|
||||||
|
for _, r := range response {
|
||||||
|
req := c.NewJsonRequest("go.mock", r.Method, map[string]interface{}{"foo": "bar"})
|
||||||
|
var rsp map[string]interface{}
|
||||||
|
|
||||||
|
err := c.Call(context.TODO(), req, &rsp)
|
||||||
|
|
||||||
|
if err != r.Error {
|
||||||
|
t.Fatalf("Expecter error %v got %v", r.Error, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log(rsp)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
17
client/mock/options.go
Normal file
17
client/mock/options.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package mock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/micro/go-micro/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Response sets the response methods for a service
|
||||||
|
func Response(service string, response []MockResponse) client.Option {
|
||||||
|
return func(o *client.Options) {
|
||||||
|
r, ok := fromContext(o.Context)
|
||||||
|
if !ok {
|
||||||
|
r = make(map[string][]MockResponse)
|
||||||
|
}
|
||||||
|
r[service] = response
|
||||||
|
o.Context = newContext(o.Context, r)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user