Is there an established best practice for separating unit tests and integration tests in GoLang (testify)? I have a mix of unit tests (which do not rely on any external resources and thus run really fast) and integration tests (which do rely on any external resources and thus run slower). So, I want to be able to control whether or not to include the integration tests when I say go test
.
The most straight-forward technique would seem to be to define a -integrate flag in main:
var runIntegrationTests = flag.Bool("integration", false
, "Run the integration tests (in addition to the unit tests)")
And then to add an if-statement to the top of every integration test:
if !*runIntegrationTests {
this.T().Skip("To run this test, use: go test -integration")
}
Is this the best I can do? I searched the testify documentation to see if there is perhaps a naming convention or something that accomplishes this for me, but didn't find anything. Am I missing something?
I was trying to find a solution for the same recently. These were my criteria:
The aforementioned solutions (custom flag, custom build tag, environment variables) did not really satisfy all the above criteria, so after a little digging and playing I came up with this solution:
The implementation is straightforward and minimal. Although it requires a simple convention for tests, but it's less error prone. Further improvement could be exporting the code to a helper function.
Usage
Run integration tests only across all packages in a project:
Run all tests (regular and integration):
Run only regular tests:
This solution works well without tooling, but a Makefile or some aliases can make it easier to user. It can also be easily integrated into any IDE that supports running go tests.
The full example can be found here: https://github.com/sagikazarmark/modern-go-application
I see three possible solutions. The first is to use the short mode for unit tests. So you would use
go test -short
with unit tests and the same but without the-short
flag to run your integration tests as well. The standard library uses the short mode to either skip long-running tests, or make them run faster by providing simpler data.The second is to use a convention and call your tests either
TestUnitFoo
orTestIntegrationFoo
and then use the-run
testing flag to denote which tests to run. So you would usego test -run 'Unit'
for unit tests andgo test -run 'Integration'
for integration tests.The third option is to use an environment variable, and get it in your tests setup with
os.Getenv
. Then you would use simplego test
for unit tests andFOO_TEST_INTEGRATION=true go test
for integration tests.I personally would prefer the
-short
solution since it's simpler and is used in the standard library, so it seems like it's a de facto way of separating/simplifying long-running tests. But the-run
andos.Getenv
solutions offer more flexibility (more caution is required as well, since regexps are involved with-run
).@Ainar-G suggests several great patterns to separate tests.
This set of Go practices from SoundCloud recommends using build tags (described in the "Build Constraints" section of the build package) to select which tests to run:
As a similar option, you could also have integration tests run by default by using a build condition
// +build !unit
, and then disable them on demand by runninggo test -tags=unit
.@adamc comments:
For anyone else attempting to use build tags, it's important that the
// +build test
comment is the first line in your file, and that you include a blank line after the comment, otherwise the-tags
command will ignore the directive.Also, the tag used in the build comment cannot have a dash, although underscores are allowed. For example,
// +build unit-tests
will not work, whereas// +build unit_tests
will.To elaborate on my comment to @Ainar-G's excellent answer, over the past year I have been using the combination of
-short
withIntegration
naming convention to achieve the best of both worlds.Unit and Integration tests harmony, in the same file
Build flags previously forced me to have multiple files (
services_test.go
,services_integration_test.go
, etc).Instead, take this example below where the first two are unit tests and I have an integration test at the end:
Notice the last test has the convention of:
Integration
in the test name.-short
flag directive.Basically, the spec goes: "write all tests normally. if it is a long-running tests, or an integration test, follow this naming convention and check for
-short
to be nice to your peers."Run only Unit tests:
this provides you with a nice set of messages like:
Run Integration Tests only:
This runs only the integration tests. Useful for smoke testing canaries in production.
Obviously the downside to this approach is if anyone runs
go test
, without the-short
flag, it will default to run all tests - unit and integration tests.In reality, if your project is large enough to have unit and integration tests, then you most likely are using a
Makefile
where you can have simple directives to usego test -short
in it. Or, just put it in yourREADME.md
file and call it the day.