Awesome FOSS Logo
Discover awesome open source software
Launched 🚀🧑‍🚀

A pattern for organizing *DD tests in golang

Categories

tl;dr – If you have a single package with lots of files, but want separate ginkgo suites, create packages for your tests and you can write your Ginkgo suites in those packages instead, and get beautiful isolation and nice orderly test-run output.

UPDATE – Looks like putting suites in their own packages has a negative effect — it messes up coverage numbers, and makes running ginkgo (with -r and –cover) fail due to Go not finding buildable Go source files. I added a dummy file in the folder, which gets rid of the error, but still ruins cover calculations (:<). Test output is still great though, and the isolation/better output is worth the lack of coverage statistics for me. Another day, another tradeoff. Recently, I’ve started using the trifecta of Ginkgo, Gomega, and Agouti for testing CasGo (a project currently under development).

While I could write a whole blog post on how useful these libraries are (it would be easier to just see for yourself by using them or perusing their walkthroughs/readme), suffice it to say that I find they are both Easy (near/at-hand) AND Simple (constructed of “small”, easy-to-understand concepts).

Sidenote – If you haven’t watched Rick Hickey’s “Simple Made Easy” talk, you should. It clarifies the distinction I am relying on above regarding “easy” things and “simple” things.

Anyway, the libaries are fantastic, but one problem I ran into was finding the right structure for the test suites.

The problem manifests partly because of go’s packaging rules, and partly because how ginkgo/gomega/agouti are built (they are built to live together with go’s packing rules and test frameworks, I’m sure the creators could have obliterated/hacked around the default behavior and done something different). The key problem is that (at present) each package essentially can only stores one suite. All the setup code (a higher order function called BeforeSuite) and teardown code (a higher order function called AfterSuite) would have to work for every test in the suite. This is all fine and well if I had very very small packages (which I maybe should), but the cas package had separation of functionality on the file level, and less on the package level. The existence of a types.go file with all the types also made this separation harder (explained in more detail below).

At first I wondered if I was just using Ginkgo wrong (and there was some easy, obvious way to create multiple suites in one package) — so I reached out on Github and filed a ticket. After being unable to resist laying some praise on onsi and his team of collaborators, I was somewhat relieved by the fact that they viewed this as a problem that others in the go community are running into — how to properly package your code when you have a types.go file that has all types (and must be included everywhere).

I chose not to re-package casgo because I thought having a types sub-package was just too much. The file-level separation inside a single package was working well for me, and I didn’t want to make a bunch of packages with just one file in them. However, one suggestion that onsi made was golden: packaging just the tests. While you might (and I, depending on day and situation) consider this a kludge, it works, and it works particularly well.

So now I get test output that looks like this:

`tl;dr – If you have a single package with lots of files, but want separate ginkgo suites, create packages for your tests and you can write your Ginkgo suites in those packages instead, and get beautiful isolation and nice orderly test-run output.

UPDATE – Looks like putting suites in their own packages has a negative effect — it messes up coverage numbers, and makes running ginkgo (with -r and –cover) fail due to Go not finding buildable Go source files. I added a dummy file in the folder, which gets rid of the error, but still ruins cover calculations (:<). Test output is still great though, and the isolation/better output is worth the lack of coverage statistics for me. Another day, another tradeoff. Recently, I’ve started using the trifecta of Ginkgo, Gomega, and Agouti for testing CasGo (a project currently under development).

While I could write a whole blog post on how useful these libraries are (it would be easier to just see for yourself by using them or perusing their walkthroughs/readme), suffice it to say that I find they are both Easy (near/at-hand) AND Simple (constructed of “small”, easy-to-understand concepts).

Sidenote – If you haven’t watched Rick Hickey’s “Simple Made Easy” talk, you should. It clarifies the distinction I am relying on above regarding “easy” things and “simple” things.

Anyway, the libaries are fantastic, but one problem I ran into was finding the right structure for the test suites.

The problem manifests partly because of go’s packaging rules, and partly because how ginkgo/gomega/agouti are built (they are built to live together with go’s packing rules and test frameworks, I’m sure the creators could have obliterated/hacked around the default behavior and done something different). The key problem is that (at present) each package essentially can only stores one suite. All the setup code (a higher order function called BeforeSuite) and teardown code (a higher order function called AfterSuite) would have to work for every test in the suite. This is all fine and well if I had very very small packages (which I maybe should), but the cas package had separation of functionality on the file level, and less on the package level. The existence of a types.go file with all the types also made this separation harder (explained in more detail below).

At first I wondered if I was just using Ginkgo wrong (and there was some easy, obvious way to create multiple suites in one package) — so I reached out on Github and filed a ticket. After being unable to resist laying some praise on onsi and his team of collaborators, I was somewhat relieved by the fact that they viewed this as a problem that others in the go community are running into — how to properly package your code when you have a types.go file that has all types (and must be included everywhere).

I chose not to re-package casgo because I thought having a types sub-package was just too much. The file-level separation inside a single package was working well for me, and I didn’t want to make a bunch of packages with just one file in them. However, one suggestion that onsi made was golden: packaging just the tests. While you might (and I, depending on day and situation) consider this a kludge, it works, and it works particularly well.

So now I get test output that looks like this:

`

Which is much nicer to me from an aesthetic aspect (than having all the specs in one huge suite) and is definitely better isolated (and could even be run in parallel, theoretically.