6

I have a function that accepts a database *mgo.Database parameter.

func myFunc(db *mgo.Database) {
// does some operations with db
}

I would like to write a unit test and pass in a mocked db object, but I'm having a very difficult time figuring out how to do that with golang. In other languages I could use there testing frameworks to do a myMock = createMock("Class to Mock"), but with Go I'm not sure how to do this.

I glanced at gomock, but wasn't sure if that is the only way, and wasn't sure how to use the mockgen tool with mgo.

I also thought maybe to write an interface that has all of the same methods as mgo.Database and pass a "mocked" object that uses the interface, and then create an object that uses the interface and passes calls through to mgo's library (similar to an ORM), but that seems like a lot of coding.

1
  • golang.org/pkg/testing it's built in the language, benchmarking too. Commented Apr 8, 2014 at 19:35

2 Answers 2

6

*mgo.Database is a pointer to a type, not an interface, you can't mock it.

As in other languages - you need to provide a level of indirection, so that you can provide a real object in production but a mock for testing. So your first step is to extract the interface that your "myFunc" uses (which methods it calls), then you can provide *mgo.Database in production and your mock (manual mock or using some mocking framework) for testing.

This free sample chapter from great book "The Art of Unit Testing" explains the steps you need to do on page 52 (chapter 3, "Using stubs to break dependencies" - "3.3 Determining how to easily test LogAnalyzer"):

http://www.manning.com/osherove/SampleChapter3.pdf

given that in Go a type implements the interface just by implementing the interface's methods - it's even easier than in other languages (like C# for example)

so the answer is as you said

to write an interface that has all of the same methods as mgo.Database and pass a "mocked" object that uses the interface, and then create an object that uses the interface and passes calls through to mgo's library (similar to an ORM), but that seems like a lot of coding.

except that you don't need to create an object that uses the interface and passes calls through to mgo's library (similar to an ORM) because *mgo.Database will implicitly satisfy your interface. So it's not a lot of coding.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks. I have been spending the entire day actually trying to implement this, but the problem is that I have to keep building these interfaces for everything the mgo.Database returns (and what those object's functions return, db.C("SomeCollection").Pipe({some commands}).Iter()) So it seems (unless I'm missing something) that I'll end up having a ton of interfaces. For example, if I want to mock a line that does SomeShop.GetCar().GetEngine().Start(), it seems I have to create ShopInterface (with GetCar()), CarInterface (with GetEngine()), and EngineInterface(with Start()).
yes, this is correct, but what are you trying to test? if you have some business logic mixed with database access, and you want to test business logic - you need to abstract database access first. myFunc should not operate raw DB object, it should use e.g. repository pattern, which will have simpler interface like db.GetCars() instead of db.C("cars").Pipe({some commands to select cars}).Iter(). Then you will be able to mock this repository easily. But you will need to provide repository implementation which will use *mgo.Database internally (for production).
Thanks. Yeah so I guess I will have to write that extra abstraction layer and have that abstraction implement the same interface as my "mock" object. Sort of wish I could just more easily mock more easily like in non typed languages but I guess that is the tradeoff. This will end up making the code better anyway. Thanks again for all your help!
If I write interface to replace mgo.Database it's method would have signature C(name string) *mgo.Collection and will not return mock. Probably all the mgo should be rewritten with interfaces for this to work. Or use wrapper as adviced here: stackoverflow.com/questions/46244007/…
-3

You can use Docker on your unit testing too.

I've created a library to help this kind of testing: https://github.com/skarllot/raiqub

Example: https://github.com/raiqub/data/blob/v0.4/mongostore/store_test.go

3 Comments

You describe integration testing; however, the question specifically asks about Unit Testing.
He is expecting to test talking to a database (external system) not a isolated unit of code then it is a integration test.
I would like to write a unit test and pass in a mocked db object. It can't be clearer than that. A unit test with a mock DB. So no integration test.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.