Open koote opened 5 months ago
The graphql client is built upon a Doer interface. You can implement any mock client or use httptest
client to intercept request and response data.
@hgiasac That is too deep and too low level, means I need to manually create a graphql.Client based on a mock HTTP client, can we add an interface to graphql.Client so it can be mocked by using gomock?
I'm not sure about your needs. It's easy to write an interface for the graphql client For example, this is a simple wrapper interface for mock test
The interface is what I am looking for, but I cannot use it because currently graphql.Client
now is a struct not an interface, for example, this is my client code:
func Query(gqlClient *graphql.Client, some vars) error {
}
Because the function expects a object of graphql.Client
, and it is a struct, I cannot pass in a mock implementation of the interface you shared.
Ideally, in my understanding, the graphql.Client
should be an interface (like you shared), not a struct:
type Client interface {
Query(ctx context.Context, q interface{}, variables map[string]interface{}, options ...graphql.Option) error
...
}
So when test my function Query
, I can pass any object in, as long as it implements the Client
interface.
However, currently the only way to test my code, is pass in a mock HTTP client when creating the graphql.Client
, and I have to read and understand the code of graphql.Client
so I can return appropriate http.Response
object that graphql.Client expects, that is why I said only providing a Doer
interface is too deep and too low level for anyone who is new to this library:
func Test(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockHTTPClient := mock.NewMockDoer(ctrl)
mockHTTPClient.EXPECT().Do(gomock.Any()).Return(...).Times(1)
gqlClient := graphql.NewClient(testServer.URL, mockHTTPClient)
// continue testing of biz logic
}
The interface is what I am looking for, but I cannot use it because currently graphql.Client now is a struct not an interface, for example, this is my client code: Because the function expects a object of graphql.Client, and it is a struct, I cannot pass in a mock implementation of the interface you shared.
Can't you replace graphql.Client
with the interface? Of course, you will need to modify your functions, but it's possible.
type Client interface {
Query(ctx context.Context, q interface{}, variables map[string]interface{}, options ...graphql.Option) error
...
}
func Query(gqlClient Client, some vars) error {
}
To clarify this library is the fork of https://github.com/shurcooL/graphql. The client was originally designed with a struct, and we haven't planned to change that design. That will be a breaking change for current users.
That means I need to define an interface and wrap the graphql.Client
within my project, I am thinking it is a bit not clean solution. But finally I managed to write my test in this way:
func Test_QueryAccounts(t *testing.T) {
filter := getDefaultFilter()
mux := http.NewServeMux()
mux.HandleFunc("/graphql", func(writer http.ResponseWriter, request *http.Request) {
body, err := io.ReadAll(request.Body)
require.NoError(t, err)
require.Equal(t, fmt.Sprintf(accountQueryBody, filter), string(body))
queryResults, err := json.Marshal(testAccounts)
require.NoError(t, err)
_, err = io.WriteString(writer, fmt.Sprintf(accountQueryResponseWithoutError, queryResults))
require.NoError(t, err)
writer.Header().Set("Content-Type", "application/json")
})
testServer := httptest.NewServer(mux)
defer testServer.Close()
myClient, err := NewClient(&Config{Endpoint: testServer.URL + "/graphql"}, WithHTTPClient(http.DefaultClient))
require.NoError(t, err)
require.NotNil(t, myClient)
accounts, err := myClient.QueryAccounts(context.Background(), filter)
require.NoError(t, err)
require.Equal(t, len(testAccounts), len(accounts))
}
graphql.Client
doesn't provide an interface, so how can I use gomock to create a mock ofgraphql.Client
for testing? Thanks.