1

I am trying to write nice unit tests for my already created REST API. I have this simple structure:

ROOT/
    config/
    handlers/
    lib/
    models/
    router/
    main.go

config contains configuration in JSON and one simple config.go that reads and parses JSON file and fills the Config struct. handlers contains controllers (i.e. handlers of respective METHOD+URL described in router/routes.go). lib contains some DB, request responder and logger logic. models contains structs and their funcs to be mapped from-to JSON and DB. Finally router contains the router and routes definition.

Now I was searching and reading a lot about unit testing REST APIs in GO and found more or less satisfying articles about how to set up a testing server, define routes and test my requests. All fine. BUT only if you want to test a single file!

My problem is now how to set up the testing environment (server, routes, DB connection) for all handlers? With the approach found here (which I find very easy to understand and implement) I have one problem: either I have to run tests separately for each handler or I have to write test suites for all handlers in just one test file. I believe you understand that both cases are not very happy (1st because I need to preserve that running go test runs all tests that succeed and 2nd because having one test file to cover all handler funcs would become unmaintainable).

By now I have succeeded (according to the linked article) only if I put all testing and initializing code into just one func per XYZhandler_test.go file but I don't like this approach as well.

What I would like to achieve is kind of setUp() or init() that runs once with first triggered test making all required variables globally visible and initialized so that all next tests could use them already without the need of instantiating them again while making sure that this setup file is compiled only for tests...

I am not sure if this is completely clear or if some code example is required for this kind of question (other than what is already linked in the article but I will add anything that you think is required, just tell me!

shadyyx
  • 15,825
  • 6
  • 60
  • 95
  • Why is it not possible to do all the set-up for your tests in an init function? – Machiel Mar 23 '16 at 12:55
  • @Machiel If I have an init function that sets everything up, I need to define global variables for one test file. This implies I cannot define the same variables in another test file. I want to make sure that I am able to run each test separately as well as to run them all at once... – shadyyx Mar 23 '16 at 14:46
  • What exactly do you mean by 'test files'? I understand you have a_test.go b_test.go, but that should not be a problem right? If you run one test it sets everything up for one test, otherwise it sets everything up for multiple tests? – Machiel Mar 23 '16 at 15:08

2 Answers2

1

Perhaps you could put the setup code that you want to use from multiple unit test files into a separate package that only the unit tests use?

Or you could put the setup code into the normal package and just use it from the unit tests.

It's been asked before but the Go authors have chosen not to implicitly supply a test tag that could be used to selectively enable function compiles within the normal package files.

WeakPointer
  • 3,087
  • 27
  • 22
  • I have tried to pull the *init* stuff to extra package, but this brings one downside - in order to make the package/file accessible, it has to be without `_test` name in the package which makes it to be built within the production code (i.e. final executable). This is not really wanted... When I pulled the init code into a `_test` package, variables set in it's `init()` func were not accessible by test files. Neither was `SetUpTests()` func which I also tried... – shadyyx Mar 23 '16 at 14:44
  • Sorry. At least when I build packages, I can control whether they end up in my production code. If my production code doesn't import the package, the package is not included. By package, I mean a go package, so the file(s) for it are in their own directory. They can't be in the same directory as the production code files. – WeakPointer Mar 23 '16 at 17:42
1

Test packages, not files!

Since you're testing handlers/endpoints it would make sense to put all your _test files in either the handlers or the router package. (e.g. one file per endpoint/handler).

Also, don't use init() to setup your tests. The testing package specifies a function with the following signature:

func TestMain(m *testing.M) 

The generated test will call TestMain(m) instead of running the tests directly. TestMain runs in the main goroutine and can do whatever setup and teardown is necessary around a call to m.Run. It should then call os.Exit with the result of m.Run

Inside the TestMain function you can do whatever setup you need in order to run your tests. If you have global variables, this is the place to declare and initialize them. You only need to do this once per package, so it makes sense to put the TestMain code in a seperate _test file. For example:

package router

import (
    "testing"
    "net/http/httptest"
) 

var (
    testServer *httptest.Server
)

func TestMain(m *testing.M)  {
    // setup the test server
    router := ConfigureRouter()
    testServer = httptest.NewServer(router)

    // run tests
    os.Exit(m.Run())
}

Finally run the tests with go test my/package/router.

fl0cke
  • 2,804
  • 1
  • 16
  • 25
  • OK, so having this `TestMain` and global variables initialized I just rely on them in my handlers tests (ofc. I have one test per handler/endpoint) and it will work? And this `os.Exit()` will be called after all tests from all packages are done? I need to be able to run the tests with `go test ./...` because this will be called from build and deploy script. – shadyyx Mar 24 '16 at 09:57
  • I am trying to achieve this with Ginkgo, but unfortunately I am experiencing some problems there as well... http://stackoverflow.com/questions/36202934/running-ginkgo-test-suite-beforesuite-setup-before-any-spec-is-ran – shadyyx Mar 24 '16 at 14:50
  • @shadyyx Yes, you can rely on them in handlers. But keep in mind that global variables are package scoped. If you test multiple packages with `go test ./...` you'll need one `TestMain` per package. And yes, os.Exit() will be called when all tests are done. – fl0cke Mar 24 '16 at 21:29
  • 1
    Thank you once more. Though I understand this is a valid answer and I'll accept it, I'll post mine as well to cover the same functionality with Ginkgo and Gomega that I am finally utilizing :-) – shadyyx Mar 25 '16 at 16:29