Last active
April 15, 2021 08:51
-
-
Save Nydan/2dada664e397d701c4a2be37533b95b2 to your computer and use it in GitHub Desktop.
Code snippet for article: Structuring Integration Testing in Golang
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package config | |
type Config struct { | |
ConnPsql string | |
} | |
// Load loads a configuration files into Config struct | |
func Load(path string) (Config, error) { | |
return Config{}, nil | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// +build it | |
package it | |
import ( | |
"database/sql" | |
"io/ioutil" | |
"net/http" | |
"github.com/nydan/integration_test/config" | |
"github.com/nydan/integration_test/migrator" | |
"github.com/nydan/integration_test/server" | |
"github.com/stretchr/testify/suite" | |
) | |
// exampleTestSuite is the basic testing suite. | |
// Here we can place all the dependency that we need for running the test. | |
// In this example we can add database client connection to test cases with DB. | |
type exampleTestSuite struct { | |
suite.Suite | |
db *sql.DB | |
cfg config.Config | |
srv *http.Server | |
} | |
// SetupTest initialize the testing suite dependency before the first test case executed. | |
// Within this step your integration test able to connect to any integrated client required | |
// For multiple test cases in a test suite, you may only need to initialize it once. | |
func (e *exampleTestSuite) SetupTest() { | |
// load configuration for your application | |
cfg, err := config.Load("application_cfg.toml") | |
e.NoError(err, "failed to load config") | |
e.cfg = cfg | |
// open connection to the database | |
db, err := sql.Open("postgres", e.cfg.ConnPsql) | |
e.NoError(err, "failed connect to DB") | |
e.NoError(db.Ping()) | |
e.db = db | |
// migrate the database schema | |
err = migrator.Up("/migrations") | |
e.NoError(err, "failed to migrate up the db schema") | |
// setup an http server and router | |
e.srv = server.NewHTTPServer() | |
go func() { | |
err := e.srv.ListenAndServe() | |
e.NoError(err, "failed to start HTTP server") | |
}() | |
} | |
// TearDownSuite is tearing down all the setup that has been initialize in the SetupTest. | |
// This tear down only happen at the end of suite life cycle. | |
func (e *exampleTestSuite) TearDownSuite() { | |
var err error | |
err = e.db.Close() | |
e.NoError(err, "failed to close db connection") | |
err = e.srv.Close() | |
e.NoError(err, "failed to shutdown HTTP server") | |
} | |
// successLoginTest contains context for the test case. The value of each field is | |
// initialize on setup() and will be the reference for clean up the data on clean(). | |
type successLoginTest struct { | |
id int64 | |
email string | |
pass string | |
} | |
// createVerifiedUser creates a user to be tested for login API. | |
func (s *successLoginTest) createVerifiedUser(e *exampleTestSuite) { | |
row := e.db.QueryRow( | |
`INSERT INTO admin (email, password, status) VALUES ($1, $2, $3) returning id`, | |
s.email, s.pass, "verified") | |
err := row.Scan(&s.id) | |
e.NoError(err, "failed to insert user") | |
} | |
func (s *successLoginTest) loginAPICall(e *exampleTestSuite) *http.Response { | |
c := http.Client{} | |
req, err := http.NewRequest(http.MethodPost, "http://localhost:8080/login", nil) | |
e.NoError(err, "failed to create a request") | |
resp, err := c.Do(req) | |
e.NoError(err, "failed to do request") | |
return resp | |
} | |
// clean cleans predefined data or produced data from the test case. | |
func (s *successLoginTest) clean(e *exampleTestSuite) { | |
_, err := e.db.Exec(`DELETE FROM admin WHERE id = $1`, s.id) | |
e.NoError(err, "failed to delete user") | |
} | |
func (s *successLoginTest) assertLoginSuccessful(e *exampleTestSuite, resp *http.Response) { | |
buf, err := ioutil.ReadAll(resp.Body) | |
defer resp.Body.Close() | |
e.NoError(err, "failed to read response body") | |
// Do all the assertion related to the response body. | |
e.NotEmpty(buf) | |
} | |
// TestSuccessLogin test case that simulate a registered user try to | |
// login into your app. This test case specifically test your login scenario, | |
// and to test that particular part means we need have a predefined data | |
// which is a verified account. | |
func (e *exampleTestSuite) TestSuccessLogin() { | |
tt := new(successLoginTest) | |
defer tt.clean(e) | |
tt.createVerifiedUser(e) | |
resp := tt.loginAPICall(e) | |
tt.assertLoginSuccessful(e, resp) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package migrator | |
// Up migrate up the sql file from the path | |
func Up(path string) error { | |
return nil | |
} | |
// Down migrate down the sql file from the path | |
func Down(path string) error { | |
return nil | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package server | |
import "net/http" | |
// NewHTTPServer create a new http Server instance | |
func NewHTTPServer() *http.Server { | |
return &http.Server{} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// exampleTestSuite is the basic testing suite. | |
// Here we can place all the dependency that we need for running the test. | |
// In this example we can add database client connection to test cases with DB. | |
type exampleTestSuite struct { | |
suite.Suite | |
db *sql.DB | |
cfg config.Config | |
srv *http.Server | |
} | |
// SetupTest initialize the testing suite dependency before the first test case executed. | |
// Within this step your integration test able to connect to any integrated client required | |
// For multiple test cases in a test suite, you may only need to initialize it once. | |
func (e *exampleTestSuite) SetupTest() { | |
// load configuration for your application | |
cfg, err := config.Load("application_cfg.toml") | |
e.NoError(err, "failed to load config") | |
e.cfg = cfg | |
// open connection to the database | |
db, err := sql.Open("postgres", e.cfg.ConnPsql) | |
e.NoError(err, "failed connect to DB") | |
e.NoError(db.Ping()) | |
e.db = db | |
// migrate the database schema | |
err = migrator.Up("/migrations") | |
e.NoError(err, "failed to migrate up the db schema") | |
// setup an http server and router | |
e.srv = server.NewHTTPServer() | |
go func() { | |
err := e.srv.ListenAndServe() | |
e.NoError(err, "failed to start HTTP server") | |
}() | |
} | |
// TearDownSuite is tearing down all the setup that has been initialize in the SetupTest. | |
// This tear down only happen at the end of suite life cycle. | |
func (e *exampleTestSuite) TearDownSuite() { | |
var err error | |
err = e.db.Close() | |
e.NoError(err, "failed to close db connection") | |
err = e.srv.Close() | |
e.NoError(err, "failed to shutdown HTTP server") | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// successLoginTest contains context for the test case. The value of each field is | |
// initialize on setup() and will be the reference for clean up the data on clean(). | |
type successLoginTest struct { | |
id int64 | |
email string | |
pass string | |
} | |
// createVerifiedUser creates a user to be tested for login API. | |
func (s *successLoginTest) createVerifiedUser(e *exampleTestSuite) { | |
row := e.db.QueryRow( | |
`INSERT INTO admin (email, password, status) VALUES ($1, $2, $3) returning id`, | |
s.email, s.pass, "verified") | |
err := row.Scan(&s.id) | |
e.NoError(err, "failed to insert user") | |
} | |
func (s *successLoginTest) loginAPICall(e *exampleTestSuite) *http.Response { | |
c := http.Client{} | |
req, err := http.NewRequest(http.MethodPost, "http://localhost:8080/login", nil) | |
e.NoError(err, "failed to create a request") | |
resp, err := c.Do(req) | |
e.NoError(err, "failed to do request") | |
return resp | |
} | |
// clean cleans predefined data or produced data from the test case. | |
func (s *successLoginTest) clean(e *exampleTestSuite) { | |
_, err := e.db.Exec(`DELETE FROM admin WHERE id = $1`, s.id) | |
e.NoError(err, "failed to delete user") | |
} | |
func (s *successLoginTest) assertLoginSuccessful(e *exampleTestSuite, resp *http.Response) { | |
buf, err := ioutil.ReadAll(resp.Body) | |
defer resp.Body.Close() | |
e.NoError(err, "failed to read response body") | |
// Do all the assertion related to the response body. | |
e.NotEmpty(buf) | |
} | |
// TestSuccessLogin test case that simulate a registered user try to | |
// login into your app. This test case specifically test your login scenario, | |
// and to test that particular part means we need have a predefined data | |
// which is a verified account. | |
func (e *exampleTestSuite) TestSuccessLogin() { | |
tt := new(successLoginTest) | |
defer tt.clean(e) | |
tt.createVerifiedUser(e) | |
resp := tt.loginAPICall(e) | |
tt.assertLoginSuccessful(e, resp) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment