rethinkdb / rethinkdb-go

Go language driver for RethinkDB

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to use mocking and session interchangeably?

sankethpb opened this issue · comments

Hello @CMogilko @dancannon and all,

Am beginner in golang and rethinkDB. I was trying to use session and mock interchangeably in production and unit testing like shown below.
Requirement:
I have a http POST handler which receives a Person struct and saves to rethink db. In production I want to pass a real session of RethinkDB and in unit test I want to pass mock db and perform further test queries on mock either directly or using other handlers.

I tried this with below sample code, but I get below error when I run unit tests.
Could you please help me here and also clarify how I would be able to achieve such a functionality?

Error:
--- FAIL: Test_PersonPost (0.00s)
exit status 2
FAIL	rethinkDBTrial	4.041s
panic: gorethink: mock: This query was unexpected:
		r.DB("person").Table("person").Insert({name="myname", age=20}) [recovered]
	panic: gorethink: mock: This query was unexpected:
		r.DB("person").Table("person").Insert({name="myname", age=20})
//file: main.go
package main

import (
	"rethinkDBTrial/db"

	"github.com/kataras/iris"
	"github.com/kataras/iris/context"
	r "gopkg.in/gorethink/gorethink.v3"
)

type person struct {
	Name string `gorethink:"name" json:"name"`
	Age  int    `gorethink:"age" json:"age"`
}

//new iris app, pass rethinkDB session or mock session here
func newApp(dbSession interface{}) *iris.Application {

	app := iris.New()

	//Post handler to post a person struct
	app.Post("/person", func(ctx context.Context) {
		//unmarshal person
		per := &person{}
		err := ctx.ReadJSON(per)

		if err != nil {
			ctx.StatusCode(iris.StatusNotAcceptable)
			ctx.WriteString(err.Error())
		} else {

			//Insert data to database, assert dbSession as QueryExecutor
			table, err := r.DB("person").Table("person").Insert(per).Run(dbSession.(r.QueryExecutor))

			if err != nil {
				ctx.StatusCode(iris.StatusExpectationFailed)
				ctx.Writef("Cannot insert %v\n", err)
			}

			table.Close()
		}
	})

	return app

}

func main() {

	//Initialize app with real db session
	app := newApp(db.GetSession())
	app.Run(iris.Addr(":8090"))

}

//file: db/db.go
package db

import (
	r "gopkg.in/gorethink/gorethink.v3"
)

var session *r.Session

func init() {
	var err error
	session, err = r.Connect(r.ConnectOpts{
		Address: "localhost:28015",
	})
	if err != nil {
		panic(err.Error)
	}

	tableName := "person"
	r.DBList().Contains(tableName).Do(r.DBCreate(tableName).Exec(session)).Run(session)


	r.DB(tableName).TableCreate(tableName).Exec(session)
}

//GetSession of DB
func GetSession() *r.Session {
	return session
}
//file: main_test.go
package main

import (
	"testing"

	"github.com/kataras/iris/httptest"
	r "gopkg.in/gorethink/gorethink.v3"
)

func Test_PersonPost(t *testing.T) {
	mockDB := r.NewMock()
	mockDB.On(r.DBCreate("person"))
	mockDB.On(r.DB("person").TableCreate("person"))
	app := newApp(mockDB)
	testServer := httptest.New(t, app)

	per := &person{"myname", 20}
	testServer.POST("/person").WithJSON(per).Expect().Status(httptest.StatusOK)

	//further assertions / queries on mock db directly or using other Get / Patch handlers
}

@sankethpb Hello! I've noticed that you call DBCreate and TableCreate in init() function.
In your unit-test package db isn't imported so there is no db.init() invocation and consequently no DBCreate and TableCreate invocation.

First of all you should learn how init function works:

  1. https://golang.org/doc/effective_go.html#init
  2. https://stackoverflow.com/questions/24790175/when-is-the-init-function-run

@sankethpb usage of init() is very bad especially for connecting db because of it cannot be tested. So you need to create db and tables in any other testable function.
Also, you need to return values from mock, see the stretchr/testify examples.
You get error because your mock expects 2 calls:

  1. DBCreate
  2. TableCreate
    But init func cannot be tested so this functions are not called. Instead of it, you run POST that calls insert that your mock doesn't expect.

thank you for your comments and suggestions. Using init function was a bad example here, this is a sample code, so please ignore this part.

Is there any slack or Gitter channels available for few more clarifications regarding this?

I think this place is not appropriate for a discussion.