I'm confused on how to get the data from mgo in gqltesting.RunTests.
I'm getting the data as expected in graphiQL, but gqltesting retrieves a FAIL. Could anyone please tell me what I'm doing wrong or how to debug this?
not found
--- FAIL: TestPost (0.00s)
--- FAIL: TestPost/post_query (0.00s)
testing.go:51: got: {"post":{"id":"","slug":"","title":""}}
testing.go:52: want: {"post":{"id":"","slug":"second-post","title":"Hello second post"}}
func TestPost(t *testing.T) {
rootSchema := graphql.MustParseSchema(gqlSchema.GetRootSchema(), &gqlResolver.Resolver{})
slug := "second-post"
title := "Hello second post"
t.Run("post query", func(t *testing.T) {
gqltesting.RunTests(t, []*gqltesting.Test{
{
Schema: rootSchema,
Query: `
{
post(slug:"` + slug + `") {
slug
title
}
}
`,
ExpectedResult: `
{
"post": {
"slug": "` + slug + `",
"title": "` + title + `"
}
}
`,
},
})
})
}
// [...]
func (r *Resolver) Post(args struct{ Slug string }) *postResolver {
oneResult := &post{}
session, collection := mongo.Get("post")
defer session.Close()
err := collection.Find(bson.M{"slug": args.Slug}).Select(bson.M{}).One(&oneResult)
if err != nil {
fmt.Println(err)
}
if s := oneResult; s != nil {
return &postResolver{oneResult}
}
return nil
}
// [...]
I have not used mongo connector in golang before.
But I am curious is there any db mocking tools for mongodb or is the connector connected with your db when running your test
Hmm I didn't realize people were using gqltesting externally.
Is this common? Have you found the package helpful?
If so, perhaps this could be a separate repository in the organization?
@OscarYuen I made a gist here and my project "lychee" is kind of an extended version of that gist. I'm quite new in Go and not sure how to achieve the connection you are talking about for running tests.
@tonyghita I found gqltesting helpful especially when testing with some hard coded mock data. Just having issues to understand how to test with real mongodb data. Some projects I stumbled upon are also using it externally. I'm not experienced enough to advise to separate the repo or not, but I definitely like the package.
Btw thanks to both of you as your projects graphql-go-example and go-graphql-starter are actually my main inspiration for learning cool new things and creating lychee.
@astenmies I do not see a problem with gqltesting. You have a problem with writing tests in general. It is very difficult to test anything either when you have global variables that are not explicitly passed to the function.
In your case, I would use the context to pass the variable "mongo".
func main() {
session, _ := GetMongo()
middleware := mongoContextMiddleware(session)
http.Handle("/graphql", middleware(cors.Default().Handler(&relay.Handler{Schema: graphqlSchema})))
...
}
func mongoContextMiddleware(mongo *mgo.Session) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, "mongo", mongo)
r = r.WithContext(ctx)
next.ServeHTTP(w, r)
})
}
}
type Resolver struct{}
func (r *Resolver) Post(ctx context.Context, args struct{ Slug string }) *postResolver {
mongo, ok := ctx.Value("mongo").(*mgo.Session) // <---
if !ok {
// Could not get "mongo" from the context.
}
// One result is a pointer to type post.
oneResult := &post{}
...
}
test:
func TestPost(t *testing.T) {
mongo := ... // <---
ctx := context.WithValue(context.TODO(), "mongo", mongo) // <---
rootSchema := graphql.MustParseSchema(gqlSchema.GetRootSchema(), &gqlResolver.Resolver{})
slug := "second-post"
title := "Hello second post"
t.Run("post query", func(t *testing.T) {
gqltesting.RunTests(t, []*gqltesting.Test{
{
Schema: rootSchema,
Context: ctx, // <---
Query: ...,
ExpectedResult: ...,
},
})
})
}
For myself, I write interfaces and pass them either through context (as above) or within a resolver:
func main() {
session, _ := GetMongo()
graphqlSchema = graphql.MustParseSchema(Schema, &Resolver{
mongo: session, // <---
})
}
type Resolver struct{
mongo *mgo.Session // <---
}
func (r *Resolver) Post(args struct{ Slug string }) *postResolver {
session, collection := r.mongo.Get("post") // <---
...
}
test:
func TestPost(t *testing.T) {
mongo := ...
rootSchema := graphql.MustParseSchema(gqlSchema.GetRootSchema(), &gqlResolver.Resolver{
mongo: mongo // <---
})
slug := "second-post"
title := "Hello second post"
t.Run("post query", func(t *testing.T) {
gqltesting.RunTests(t, []*gqltesting.Test{
{
Schema: rootSchema,
Query: ...,
ExpectedResult: ...,
},
})
})
}
But it is better to separate the database and resolvers(code from my project):
func (r *Resolver) Login(ctx context.Context, args struct {
Email string
Password string
}) (*UserTokenResolver, error) {
return ResolveLoginUser(ctx, args.Email, args.Password)
}
func ResolveLoginUser(ctx context.Context, email, password string) (*UserTokenResolver, error) {
stor, ok := storage.FromContext(ctx)
if !ok || stor.User == nil {
logrus.Panic("[ResolveLoginUser] Storage not initialed")
return nil, nil
}
trimEmail := emailx.Normalize(email)
trimPassword := strings.TrimSpace(password)
emailErr := emailx.ValidateFast(trimEmail)
if emailErr != nil || trimPassword == "" {
return nil, errNotValidLogin
}
// get user from storage
user, err := stor.User.GetByEmail(trimEmail)
if err != nil {
logrus.Error(err)
return nil, errNotValidLogin
}
if user == nil || !user.ValidatePassword(password) {
return nil, errNotValidLogin
}
return ResolveUserToken(ctx, &UserResolver{User: user})
}
Thanks a lot @GhostRussia, this is very helpful
I accidentally realized that the test I initially posted in this issue is actually working when the containing file is placed alongside main.go (top level of the app). Thus without context...
Does anyone pls have more insight on why?
You've probably got some global state leaking between tests in the same package. Is there another test in the top level?
I don't think this issue is specific to this library, so I'll close it for now.
Most helpful comment
@astenmies I do not see a problem with gqltesting. You have a problem with writing tests in general. It is very difficult to test anything either when you have global variables that are not explicitly passed to the function.
In your case, I would use the context to pass the variable "mongo".
test:
For myself, I write interfaces and pass them either through context (as above) or within a resolver:
test:
But it is better to separate the database and resolvers(code from my project):