Writing integration tests with MongoDB support
One of the things I was looking for while writing applications in GO at Newest Industry was a way of testing my http handlers with MongoDB support. Until recently I haven’t found a nice way of doing that (and yes, I have been blind all this time). The current way of testing was setup primarily to use within our PHP projects. Because we couldn’t find a better way to do it at the time in GO we adopted the same test suite in those project as well, but it felt inefficient and a little bit shaky (although it worked and did exactly what we wanted) so I went on and found a better way of doing this within our GO projects.
The way we solved this before is letting our Bamboo agent run a single MongoDB instance which we used to test all our applications with and started the GO app with a “bamboo” config file which pointed to the db hostname and database in te background. Then, we ran a behat test suite against a preconfigured Nginx vhost and when the test was done the app process was simply killed.
This did exactly what we needed (testing the API from input to output) so we could ensure delivery would go well. But it was super inefficient. Not only did we have to keep up an entire webstack and keep it in sync with our production environments, but we also used a (slow) PHP testsuite for our GO application. On top of that, I had to build and run the app in a background process while hoping the Behat run didn’t exit too hard otherwise the process would never get killed (which also happened a few times).
So out of frustration I started digging again and I found this post again: “Testing Your (HTTP) Handlers in Go” which defines beautifully how one can use the httptest package to do a full integration test on a handler. But this still wasn’t enough for me as I needed a working MongoDB instance to connect to and I wanted to see if there was a better way than to do a full setup on the server. And there is…
Hidden deep within the source code of mgo lies a small and supereasy package called dbtest (formerly known as testserver). At least, that’s what I keep telling myself because I‘ve never come across this package before, even though I’m using mgo for a long time. So to help others so they don’t replicate my own short-sightedness I thought I’d explain here how to use it.
In the example I’ve created a simple http app with 2 handlers. On to get all pages from MongoDB, and one where you can get a specific one based on slug. The list can be requested by the uri “/” and the page by uri “/page?slug=slug”. For example purposes I omitted all other crud actions. I tried to keep the example as clear as possible so I only used std lib packages, except for MGO of course. I’ll skip the handlers and the main because I think they are pretty self explanatory.
I’ve created an init_test.go file which does the main setup for me. This does 2 things.
Wrapping the testsuite
In the init_test.go you’ll see a TestMain func. This is to wrap the testsuite with the functionality of creating and destroying the MongoDB instance and connection. The package needed for this is:
go get gopkg.in/mgo.v2/dbtest
It is really as easy as that. Now you have full MongoDB support…
Inserting the fixtures
As long as I don’t have an insane amount of fixtures to work with I’d like to insert them directly in my TestMain process. This gives me the freedom of keeping al info of the testdata in one place and the ability to select specific data to test against. This I do by creating a map[string]Type (so I can easily select them later) and loop through to insert them.
I also add a reInsertFixtures() func so I can decide per test to reload my fixtures if needed (for instance to make sure a previous test didn’t delete one of these).
And now writing the tests is super simple! By using the httptest package from the net/http std lib you’re able to create a recorder and select a specific handler to test from. I’ve included examples in the gist below which contain some happy and not-so-happy path flows which I use usually. Using
page := pages[“ding”]
enables me to easily test a specific set of data in my test for assertion.
This enables us to completely test our entire GO codebase and ensure our clients the validity of the workings of our API’s! The full example is available on my GitHub page of course.