unit-testinggotesting

Structuring tests in subfolders with Golang


The project I started with go-blueprint has modules like:

./cmd
  ./api
    ./main.go (main)
./internal
  ...
  ./server
    ...
    ./server.go (server -> app/internal/server)
    ...
  ...
./tests
  ./handler_test.go

(Of course, there are a lot more files and folders inside internal.)

Currently, handler_test.go tests ./internal/server/server.go, and doing go test will start that test.

I am used to have tests going in a mirror folder structure, so I would like to have

./internal
  ./server
    ./server.go
./tests
  ./server
    ./server_test.go

Doing that, I can do go test app/tests/server and it does run server_test.go, but no other test.

I tried using t.Run, and I would like to import test functions, however it does not work:

// ./tests/server_test.go
package tests

import (
  testServer "app/tests/server"  // ❌ could not import app/tests/server (no required module provides package "app/tests/server")

  "testing"
)

func TestServer(t *testing.T) {
  t.Run("Testing server routes", testServer.TestBaseRoutes)
}

Maybe I do not understand the paradigm, coming from JUnit, PHPUnit, Jest, etc. but I don't seem to understand how Go structures tests when the app being used is of medium size with, perhaps, dozens of test files.

Is what I'm doing even possible?


Solution

  • Go effectively differentiates between white-box testing and black-box testing. With white-box testing, the tests live within the same package as the code you want to test so you have access to the private internals. With black-box testing, a little underappreciated in online articles unfortunately, you place the test code in it's own isolated package so the test code only has access to the public interface of the code you want to test.

    The documentation makes specific note of this split.

    https://pkg.go.dev/testing

    I quote:

    The test file can be in the same package as the one being tested, or in a corresponding package with the suffix "_test".

    If the file is in a separate "_test" package, the package being tested must be imported explicitly and only its exported identifiers may be used. This is known as "black box" testing.

    package abs_test
    
    import (
      "testing"
    
      "path_to_pkg/abs" 
    )
    
    func TestAbs(t *testing.T) {
      // test public abs stuff
    }
    

    So that is the convention that Go defines.