TestMain for all tests? - go

I have a fairly large project with many integration tests sprinkled throughout different packages. I'm using build tags to separate unit, integration and e2e tests.
I need to do some setup before running my integration and e2e tests, so I put a TestMain function in a main_test.go file in the root directory. It's pretty simple:
//go:build integration || e2e
// +build integration e2e
package test
import (
...
)
func TestMain(m *testing.M) {
if err := setup(); err != nil {
os.Exit(1)
}
exitCode := m.Run()
if err := tearDown(); err != nil {
os.Exit(1)
}
os.Exit(exitCode)
}
func setup() error {
// setup stuff here...
return nil
}
func tearDown() error {
// tear down stuff here...
return nil
}
However, when I run test:
$ go test -v --tags=integration ./...
testing: warning: no tests to run
PASS
# all of my subdirectory tests now run and fail...
I really don't want to write a TestMain in each package that requires it and was hoping I could just get away with one in the root. Is there any solution that you could suggest? Thanks.
The only alternative I can think of is setting up everything outside of code and then running the integration tests. Maybe some shell script that does setup and then calls $ go test?

The go test ./... command compiles a test binary for each package in the background and runs them one by one. This is also the reason you get a cannot use -o flag with multiple packages error if you attempt to specify an output. This is the reason code in your main package doesn't effect your sub packages.
So the only way to get this to work is to put all your setup logic in sort of "setup" package and call the shared code from all of your sub-packages(still a lot of work, I know).

Trying to avoid code repetition, I used a function that makes the setup/teardown and evaluates a function as a test.
The function should look like this
func WithTestSetup(t *testing.T, testFunction func()) {
// setup code
testFunction()
// teardown code
}
I use the t *testing.T argument to report errors in setup or teardown, but it can be omitted.
Then in your tests you can make:
func TestFoo(t *testing.T) {
WithTestSetup(
t, func() {
if err := Foo(); err != nil {
t.Fatal(err)
}
},
)
}
Just call WithTestSetup if needed, looks easier for me than add a bunch of TestMains on the project.

Related

How to Mock inner methods in GoLang

e.g
type test struct { // few fields}
func (t *test) createresource(res1 string,res2 string)error {
//doing some task
t.createsubresource(res1)
}
func (t *test)createsubresource(res1 string)error{
//perform some task
}
I want to write test function for createresource , how can I mock t.createsubresource(res1) call. This is legacy code and I don't have permission to modify any above function.
Your mock can be done using interfaces, as for example:
main.go
package main
type TestInterface interface {
CreateResource(res1 string, res2 string) error
CreateSubresource (res1 string) error
}
func main() {
DoSomething(new(Test))
}
func DoSomething(t TestInterface) {
t.CreateResource()
}
main_test.go
package main
import "testing"
type TestMock struct {}
func (tm *TestMock) CreateResource(res1 string, res2 string) error {
return nil
}
func (tm *TestMock) CreateSubresource(res1 string) error {
return nil
}
func TestDoSomething(t *testing.T) {
err := DoSomething(new(TestMock))
//... do your assertions
}
Why does it works like that?
Calling a function that depends on a specific structure does not allow you to inject alternatives to it, that's why a solution using interface needs to be created. By having an interface, just implement a new structure that matches that interface and pass it as a dependency injection to the procedure that will be tested.
Also, check this out:
There is no easy way, by default, to just point your original structure and tell Go to make a mock from it. Maybe some 3rd party lib can do it (but I didn't saw that yet).
In go, public and private declarations are defined by the first letter as uppercase. By the lower cases declarations in your sample I've noticed that everything is private.
Usually it is not a good practice to test private methods. There are a lot of discussions about this topic, you can take a look in this one here
There are also some support libs to make assertions and mocks like for example stretchr/testify, please make a research first.
I hope that it helps you.

Set up database for integration tests with TestMain across multiple packages

I am trying to write database integration tests in my go application for my repositories files.
My idea was to leverage the TestMain function to do the database bootstrap before the tests are run.
Example:
test/integration/integration_test.go
// +build integrationdb
package main
func TestMain(m *testing.M) {
flag.Parse()
// setup database
setupDB()
// run tests
os.Exit(m.Run())
}
Because this is global to all my integration tests I have placed this code in the test/integration package.
Then in each module/package of my application, together with my repository.go code, I have a repository_test.go with my test code:
// +build integrationdb
package todo_test
import (
"github.com/brpaz/go-api-sample/internal/todo"
"github.com/brpaz/go-api-sample/test/testutil"
"github.com/stretchr/testify/assert"
"testing"
)
func TestPgRepository_CreateTodo(t *testing.T) {
db, err := testutil.GetTestDBConnection()
if err != nil {
t.Fatal(err)
}
repo := todo.NewPgRepository(db)
err = repo.CreateTodo(todo.CreateTodo{
Description: "some-todo",
})
assert.Nil(t, err)
}
The setup works fine, the issue is that when running the tests, it says "testing: warning: no tests to run" and the tests are still executed, without running the TestMain function.
It seems that the TestMain function is per package? Is that true? Any workarounds?
I could put all the test files into a unique and separate package (I do that for higher-level tests like acceptance and smoke tests), but since I am testing each repository individually, It doesn't differ that much from a unit test, so I think it makes sense to keep then together with the code.
I guess I could create a TestMain per package and have some global variable in my global testutils package that kept if the database was initialized and run the setup if not set. Not really a fan of globals.
The other alternative I see is to move the setup code outside of the test logic. I prefer it to keep tightly integrated with Go as it makes it easier to defer cleanup functions for example, after the tests run.
Maybe I can create my own "integration test runner" command, that would run the setup code and then call "go test" programmatically?
Any other ideas?
The tests of each package can be run independently as they should. The only missing link was the bootstrap and teardown of the test database.
I decided to create a command in my application that will bootstrap the tests database and then running the "go test".
I could have this bootstrap logic separated, let´s say in a bash script, but I feel this way makes it easier.
Here is the code I ended up with for reference:
test/integration/db/main.go
func main() {
flag.Parse()
log.Println("Setup DB")
// setupDB creates a new test database and runs the application migrations before the tests start.
setupDB()
log.Println("Running Tests")
// calls go test to execute the tests.
runTests()
// delete the test database.
teardownDB()
}
func runTests() {
// TODO this flags can be passed from the original command instead.
cmd := exec.Command("go", "test", "-v", "--tags", "integrationdb", "-p", "1", "./...")
cmd.Env = os.Environ()
cmd.Stdout = os.Stdout
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
if err := cmd.Wait(); err != nil {
log.Fatal(err)
}
}

Using testing.T in TestMain

There are a few test cases I want to run for which there is a need to start a GRPC mock server. I am using gomock library for this. To start the server, I have to pass a variable of type testing.T to this function - gomock.NewController(). Since this is a kind of initialization for all the test cases, I want to do this in TestMain. But TestMain has access to only testing.M So how do I handle this case? Create a new testing.T structure in TestMain? Will it work?
It sounds like you are looking for a BeforeEach pattern. You don't have access to a testing.T object in TestMain because this is something more of a place to do initialization before and after the test suite runs.
There are a few frameworks out there that can give you a BeforeEach cheap:
Ginkgo
Onpar
to name a few.
You could also hand roll your own:
type test struct{
ctrl *gomock.Controller
mockFoo *MockFoo
// ...
}
func beforeEach(t *testing.T) test {
ctrl := gomock.NewController(t)
return test {
ctrl:ctrl,
mockFoo: NewMockFoo(ctrl),
}
}
func TestBar(t *testing.T) {
test := beforeEach(t)
// ...
}
func TestBaz(t *testing.T) {
test := beforeEach(t)
// ...
}

Different packages with different config props - Functional option

I have an application which needs configuration and I’ve created a configuration struct and I’m entering the configuration as a parameter to the function. The problem is that the configuration struct becomes bigger (like monolith) and bigger and I move the config to different functions in my app and which doesn’t need all the fields, just few of them. My question is if there is better approach to implement it in Go.
After struggling to find good way I’ve found this article (which a bit old but hopefully still relevant) and I wonder how and if I can use it to solve my problem.
Functional options instead of config struct
https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis
I need to inject some configuration properties to my application in
For example for function run (which is entry point ) I need to inject the log level and some other env variable like port host
For function build I need to “inject” the build flavor and build type etc.
Any example for my content will be very helpful
How to structure it in the code ?
How to implement it?
update
I need some E2E example how can I use the functional approach for different configs in the same package and other packages
It sounds like you're looking for an alternative to passing around the same configuration monolith structure to every package and every function. There are many solutions to this problem (more than I'm going to list here), and which one is right for you requires more knowledge of your code and your goals than we have, so it's probably best if you decide. And it sounds like you're wondering whether Dave Cheney's post on functional options provides a solution and how to apply it.
If your application's configuration is static in that it's not likely to change (mutate) through different threads of execution, and you don't need to create multiple instances with different configurations in the same main, then one option is package level variables and package initialization. If you object to exported package variables, you can use unexported package variables and control access via exported functions. Say run and build are two different packages:
// package main
import(
"github.com/profilename/appname/build"
"github.com/profilename/appname/run"
)
func main() {
// do something to get configuration values
build.Initialize(buildFlavor, buildType)
// any post-build-initialize-pre-run-initialize stuff
run.Initialize(logLevel, port, host)
// other processing
build.PreBuild("title") // other build functions may rely on configuration
build.Build()
// other stuff
run.ReadFiles(f1, f2)
run.Validate(preferredBackupPort) // port availability, chance to log.Fatal out
run.Run()
// cleanup
}
// package run
var Host string
var LogLevel, Port int
init() {
Host = `localhost`
Port = 8888
Loglevel = 1
}
func Initialize(logLevel, port int, host string) {
// validation, panic on failure
LogLevel = logLevel
Host = host
Port = port
}
func Run() {
// do something with LogLevel, Host, Port
}
But that doesn't solve the problem addressed in Dave Cheney's post. What if the user is running this without host, port, or buildType (or other configuration variables), because he doesn't need those features? What if the user wants to run multiple instances with different configurations?
Dave's approach is primarily intended for situations where you will not use package-level variables for configuration. Indeed, it is meant to enable several instances of a thing where each instance can have a different configuration. Your optional configuration parameters become a single variadic parameter where the type is a function that modifies a pointer to the thing being configured. For you, that could be
// package run
type Runner struct {
Port int
// rest of runner configuration
}
func NewRunner(options ...func(*Runner)) (runner *Runner, err error) {
// any setup
for _, option := range options {
err = option(runner)
if err != nil {
// do something
}
}
return runner, err
}
// package main
func main() {
// do something to get configuration values
port := func(runner *Runner) {
runner.Port = configuredPort
}
// other configuration if applicable
runner := run.NewRunner(port)
// ...
In a way, Dave's approach appears targeted at packages that will be used as very flexible libraries, and will provide application interfaces that users might wish to create several instances of. It allows for main definitions that launch multiple instances with different configurations. In that post he doesn't go into detail on how to process configuration input in the main or on a configuration package.
Note that the way the port is set in the resulting code above is not very different from this:
// package run
type Runner struct {
Port int
// rest of runner configuration
}
// package main, func main()
runner := new(run.Runner)
runner.Port = configuredPort
which is more traditional, probably easier for most developers to read and understand, and a perfectly fine approach if it suits your needs. (And you could make runner.port unexported and add a func (r *Runner) SetPort(p int) { r.port = p } method if you wanted.) It is also a design that has the potential, depending on implementation, to deal with mutating configuration, multiple threads of execution (you'll need channels or the sync package to deal with mutation there), and multiple instances.
Where the function options design Dave proposed becomes much more powerful than that approach is when you have many more statements related to the setting of the option that you want to place in main rather than in run -- those will make up the function body.
UPDATE Here's a runnable example using Dave's functional options approach, in two files. Be sure to update the import path to match wherever you put the run package.
Package run:
package run
import(
"fmt"
"log"
)
const(
DefaultPort = 8888
DefaultHost = `localhost`
DefaultLogLevel = 1
)
type Runner struct {
Port int
Host string
LogLevel int
}
func NewRunner(options ...func(*Runner) error) (runner *Runner) {
// any setup
// set defaults
runner = &Runner{DefaultPort, DefaultHost, DefaultLogLevel}
for _, option := range options {
err := option(runner)
if err != nil {
log.Fatalf("Failed to set NewRunner option: %s\n", err)
}
}
return runner
}
func (r *Runner) Run() {
fmt.Println(r)
}
func (r *Runner) String() string {
return fmt.Sprintf("Runner Configuration:\n%16s %22d\n%16s %22s\n%16s %22d",
`Port`, r.Port, `Host`, r.Host, `LogLevel`, r.LogLevel)
}
Package main:
package main
import(
"errors"
"flag"
"github.com/jrefior/run" // update this path for your filesystem
)
func main() {
// do something to get configuration values
portFlag := flag.Int("p", 0, "Override default listen port")
logLevelFlag := flag.Int("l", 0, "Override default log level")
flag.Parse()
// put your runner options here
runnerOpts := make([]func(*run.Runner) error, 0)
// with flags, we're not sure if port was set by flag, so test
if *portFlag > 0 {
runnerOpts = append(runnerOpts, func(runner *run.Runner) error {
if *portFlag < 1024 {
return errors.New("Ports below 1024 are privileged")
}
runner.Port = *portFlag
return nil
})
}
if *logLevelFlag > 0 {
runnerOpts = append(runnerOpts, func(runner *run.Runner) error {
if *logLevelFlag > 8 {
return errors.New("The maximum log level is 8")
}
runner.LogLevel = *logLevelFlag
return nil
})
}
// other configuration if applicable
runner := run.NewRunner(runnerOpts...)
runner.Run()
}
Example usage:
$ ./program -p 8987
Runner Configuration:
Port 8987
Host localhost
LogLevel 1
I use this to define per package Config Structs which are easier to manage and are loaded at the app start.
Define your config struct like this
type Config struct {
Conf1 package1.Configuration `group:"conf1" namespace:"conf1"`
Conf2 package2.Configuration `group:"conf2" namespace:"conf2"`
Conf3 Config3 `group:"conf3" namespace:"conf3"`
GeneralSetting string `long:"Setting" description:"setting" env:"SETTING" required:"true"`
}
type Config3 struct {
setting string
}
And use "github.com/jessevdk/go-flags" to pass either --config3.setting=stringValue cmd arguments, or ENV variables export CONFIG3_SETTING=stringValue:
type Configuration interface {}
const DefaultFlags flags.Options = flags.HelpFlag | flags.PassDoubleDash
func Parse(cfg Configuration) []string {
args, _ := flags.NewParser(cfg, DefaultFlags).Parse()
return args
}
And your main should look something like this:
func main() {
// Parse the configuration.
var cfg Config
Parse(&cfg)
service := NewService(cfg.Conf3.Setting)
}

setting a variable from test file in golang

i'm trying to set a variable from my unit tests file
main_test.go
var testingMode bool = true
main.go
if testingMode == true {
//use test database
} else {
//use regular database
}
If I run "go test", this works fine. If I do "go build", golang complains that testingMode is not defined (which should be the case since tests aren't part of the program).
But it seems if I set the global variable in main.go, I'm unable to set it in main_test.
What's the correct way to about this?
Try this:
Define your variable as global in main.go:
var testingMode bool
And then set it to be true in your test file main_test.go:
func init() {
testingMode = true
}
Pierre Prinetti's Answer doesn't work in 2019.
Instead, do this. It's less then ideal, but gets the job done
//In the module that you are testing (not your test module:
func init() {
if len(os.Args) > 1 && os.Args[1][:5] == "-test" {
log.Println("testing")//special test setup goes goes here
return // ...or just skip the setup entirely
}
//...
}

Resources