I have a method which implements the a database interface, that method inserts an 'object' into the database.
type database interface {
createLog(logDoc) (bool, error)
}
type mongo struct {
database string
collection string
}
func (m mongo) createLog(l logDoc) (bool, error) {
s, err := mgo.Dial("mongo")
defer s.Close()
if err != nil {
return false, err
}
err = s.DB(m.database).C(m.collection).Insert(l)
if err != nil {
return false, err
}
return true, nil
}
I want to be able to test the createLog method, how can I do this? should it be an integration test? if so what would be the your approach?
Thank you in advance
I just thought after watching the video & pondering this for a little bit and if nobody answered I would answer my own question.
This video resulted me in not writing any integration tests, rather writing unit tests. and creating stubs which allowed me to modify the way the interface behaved allowing me to tests when the database behaves correctly and incorrectly.
If you can think of a better way of describing what I said then please do :)
Related
I want to write a golang function which return the service according to the input:
func GetService(service string) <what to write here> {
session, _ := session.NewSession(&aws.Config{Region: aws.String(Region)})
switch {
case service == 'ecr':
var svc *ecr.ECR
svc = ecr.New(session)
return svc
case service == 'ecs':
var svc *ecs.ECS
svc = ecs.New(session)
return svc
}
}
It is kind of factory for aws services, but what is their common type which has to be returned?
What you can do is write two functions where each will return either of these.
Other option is to return an interface and let the user cast the value as he knows what it is. Optionally interface can have some methods defined if object share methods with same name.
Last approach that I see is most fitting is a modular approach that I will also demonstrate. But with pseudo code.
func NewSession
session, err = (create session)
(handle error)
return session
// now lets use this
func MyUseCase
ecr = NewEcr(NewSession())
(use ecr)
The thing you are abstracting in your code should not be abstracted at all. User should create his instance as it is simpler then casting it and even faster.
The main goal of abstraction is simplify top level code and eliminate repetition. Keep that in mind.
I have following code
func (s *MyRepo) InsertOrder(ctx context.Context, orderID string) error {
query := `INSERT INTO orders (orderID) VALUES (?)`
stmt, err := s.db.RawDatabase().PrepareContext(ctx, query)
if err != nil {
return err
}
defer stmt.Close()
_, err = stmt.ExecContext(ctx, orderID)
if err != nil {
//log err
}
return err
}
And the corresponding test case is
func TestMyRepo_InsertOrder_Success(t *testing.T) {
orderID := "orderID"
mockDB, repo := getDBStore()
query := `[INSERT INTO orders (orderID) VALUES (?)]`
mockDB.ExpectPrepare(query).
ExpectExec().
WithArgs(orderID).
WillReturnResult(sqlmock.NewResult(1, 1)).
WillReturnError(nil)
err := repo.InsertOrder(context.Background(), orderID)
assert.Nil(t, err)
}
But this doesn't test if defer stmt.Close() has been called or not (which gets called once the function ends). How can I test this?
It looks like you are making use of data-dog's sqlmock package, so you ought to be able to use ExpectClose() to register the expectation that the database will be closed, and ExpectationsWereMet() to collect this information.
If you're using some other package, feel free to link it; there's probably something similar available, and worst-case you can write your own wrapper around their wrapper. Making sure that a particular method of a particular dependency was called is a fairly common desire when developers write tests using mocks, so most of the better mock packages will go out of their way to provide some sort of API to check that.
As noted in the comments on this question, tests of this nature are often of somewhat questionable value and can seem like they exist more to increase a dubious metric like % code coverage than to increase code reliability or maintainability.
we are using gopkg.in/mgo.v2/bson to talk with mongo, and its API populates passed structures instead returning results, for example:
func (p *Pipe) One(result interface{}) error {...
Problems occurs when I want to mock / test code which is using that. I want to both mock this execution and somehow get pupulated value in 'result'.
Currently test has:
query.EXPECT().One(gomock.Any())
So as you can see I dont get any value, I just configure gomock to check that when I run my method then query.One has to be called.
I cannot pass structure like
mystruct := MyStruct{}
query.EXPECT().One(&mystruct)
because mystruct in test code and in real code is different and verifing mock will fail (references are different). Im looking for something similar to mockito's argument captor:
https://static.javadoc.io/org.mockito/mockito-core/2.6.9/org/mockito/ArgumentCaptor.html
This can be achieved via Do.
Copy & Paste of Github example from poy.
var capturedArgs []int
someMock.
EXPECT().
SomeMethod(gomock.Any()).
Do(func(arg int){
capturedArgs = append(capturedArgs, arg)
})
Ref: https://github.com/golang/mock/pull/149
This project can help you: https://github.com/bouk/monkey.
You can replace a function and use a bool variable to check the use.
called := false
monkey.Patch(package.One, func(result interface{}) error {
if result == expected {
called := true
return nil
}
return errors.new("not expected")
})
Dont't forget to restore your original function.
defer monkey.Unpatch(package.One)
I'm rebuilding an app that supports customer specific templating (themes) from node.js to Go.
I'm currently using render to render my template files but what I actually need to do access templates that are stored in an object store such as Cloudfiles.
In node.js I've done this with express and I'm overriding the render() method but I've not been able to figure out how to do this in Go.
I essentially need to do something like this:
func (c *Controller) MyRouteHandler (rw http.ResponseWriter, req *http.Request) {
// retrieve the store from the context (assigned in middleware chain)
store := context.Get(req, "store").(*Store)
... do some stuff like load the entity from the database
// retrieve the template from the Object store and
// create the template instance (template.New("template").Parse(...))
tpl := c.ObjectStore.LoadTemplate(store, entity.TemplateFile)
// I know render's .HTML function takes the path to the template
// so I'll probably need to show the html a different way
c.HTML(rw, http.StatusOK, tpl, &PageContext{Title: "My Page", Entity: &entity})
}
I can dynamically include sub-templates by doing something like this if needed: http://play.golang.org/p/7BCPHdKRi2 but it doesn't seem like a great way if I'm honest.
I've searched for a solution to this but keep hitting road blocks. Any advice / assistance would be great.
Edit:
In essence, I'm asking the following:
How can I load a specific template from a datastore on a per-request basis.
How can I then send that as a response to the client
How can I load a specific template from a datastore on a per-request basis.
//take HTTP for example:
resp, err := http.Get("http://mytemplates.com/template1")
if err != nil {
// handle error
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
templateString := string(body)
How can I then send that as a response to the client
tmpl, err := template.New("name").Parse(templateString)
tmpl.Execute(rw, &yourDataModel{})
I am currently trying to test a piece of my code that runs a query on the datastore before putting in a new entity to ensure that duplicates are not created. The code I wrote works fine in the context of the app, but the tests I wrote for that methods are failing. It seems that I cannot access data put into the datastore through queries in the context of the testing package.
One possibility might lie in the output from goapp test which reads: Applying all pending transactions and saving the datastore. This line prints out after both the get and put methods are called (I verified this with log statements).
I tried closing the context and creating a new one for the different operations, but unfortunately that didn't help either. Below is a simple test case that Puts in an object and then runs a query on it. Any help would be appreciated.
type Entity struct {
Value string
}
func TestEntityQuery(t *testing.T) {
c, err := aetest.NewContext(nil)
if err != nil {
t.Fatal(err)
}
defer c.Close()
key := datastore.NewIncompleteKey(c, "Entity", nil)
key, err = datastore.Put(c, key, &Entity{Value: "test"})
if err != nil {
t.Fatal(err)
}
q := datastore.NewQuery("Entity").Filter("Value =", "test")
var entities []Entity
keys, err := q.GetAll(c, &entities)
if err != nil {
t.Fatal(err)
}
if len(keys) == 0 {
t.Error("No keys found in query")
}
if len(entities) == 0 {
t.Error("No entities found in query")
}
}
There is nothing wrong with your test code. The issue lies in the Datastore itself. Most queries in the HR Datastore are not "immediately consistent" but eventually consistent. You can read more about this in the Datastore documentation.
So basically what happens is that you put an entity into the Datastore, and the SDK's Datastore "simulates" the latency that you can observe in production, so if you run a query right after that (which is not an ancestor query), the query result will not include the new entity you just saved.
If you put a few seconds sleep between the datastore.Put() and q.GetAll(), you will see the test passes. Try it. In my test it was enough to sleep just 100ms, and the test always passed. But when writing tests for such cases, use the StronglyConsistentDatastore: true option as can be seen in JonhGB's answer.
You would also see the test pass without sleep if you'd use Ancestor queries because they are strongly consistent.
The way to do this is to force the datastore to be strongly consistent by setting up the context like this:
c, err := aetest.NewContext(&aetest.Options{StronglyConsistentDatastore: true})
if err != nil {
t.Fatal(err)
}
Now the datastore won't need any sleep to work, which is faster, and better practice in general.
Update: This only works with the old aetest package which was imported via appengine/aetest. It does not work with the newer aetest package which is imported with google.golang.org/appengine/aetest. App Engine has changed from using an appengine.Context to using a context.Context, and consequently the way that the test package now works is quite different.
To compliment #JohnGB's answer in the latest version of aetest, there are more steps to get a context with strong consistency. First create an instance, then create a request from that instance, which you can use to produce a context.
inst, err := aetest.NewInstance(
&aetest.Options{StronglyConsistentDatastore: true})
if err != nil {
t.Fatal(err)
}
defer inst.Close()
req, err := inst.NewRequest("GET", "/", nil)
if err != nil {
t.Fatal(err)
}
ctx := appengine.NewContext(req)