Testing
Run tests
Search recursively and run the ALL tests:
go test ./...
Search and run tests on a specific folder:
go test <path>
Run specific test suite:
go test <path> -run TestSuitName
# or
go test ./... -run TestSuitName
The biggest third-part testing package that helps writing assertions
, mocks
and testing suite interfaces and functions.
Assertions with assert
package will always run ALL the assertions when return all the ones that failed.
If you want a behavior that stops the tests at the first fail use Require.
func TestSomething(t *testing.T) {
// Opcionally pass an error message if the test fails
assert.Equal(t, 12, 12, "they should be equal")
// If error message not passed, testify will generate one very detailed
assert.Nil(t, nil)
}
If writing multiple assets, declare it at the top, to avoid having to pass t
at each one of them.
func TestSomething(t *testing.T) {
assert := assert.New(t)
a, err := ...
assert.Nil(err)
}
Require
The require
package provides the same global functions as the assert
, but they terminate the tests at the first assertion fail.
func TestSomething(t *testing.T) {
require := require.New(t)
a, err := ...
require.Nil(err)
}
This package provides a mechanism for easily writing mock objects that can be used in place of real objects.
Have in mind that mocks that are created in SetupSuit
are shared between tests.
Instead recreate mocks for each test with SetupTest
.
type PriceProvider interface {
Latest() (*PriceData, error)
List(time.Time) ([]*PriceData, error)
}
type PriceProviderMock struct {
mock.Mock
}
func (mock *PriceProviderMock) Latest() (*PriceData, error) {
args := mock.Called()
var r0 *PriceData
if rf, ok := args.Get(0).(func() *PriceData); ok {
r0 = rf()
} else {
if args.Get(0) != nil {
r0 = args.Get(0).(*PriceData)
}
}
var r1 error
if rf, ok := args.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = args.Error(1)
}
return r0, r1
}
func (mock *PriceProviderMock) List(date time.Time) ([]*PriceData, error) {
args := mock.Called(date)
var r0 []*PriceData
if rf, ok := args.Get(0).(func(time.Time) []*PriceData); ok {
r0 = rf(date)
} else {
if args.Get(0) != nil {
r0 = args.Get(0).([]*PriceData)
}
}
var r1 error
if rf, ok := args.Get(1).(func(time.Time) error); ok {
r1 = rf(date)
} else {
r1 = args.Error(1)
}
return r0, r1
}
func (suite *UnitTestSuit) TestCalculate() {
// db := ???
// priceProvider := stocks.NewPriceProvider(db)
// calculator := NewPriceIncreaseCalculator(priceProvider)
priceProviderMock mocks.PriceProvider{}
calculator := NewPriceIncreaseCalculator(priceProviderMock)
priceProviderMock.On("List", mock.Anything).Return([]*stocks.PriceData{
{
Timestamp: time.Now(),
Price: 2.0
},
{
Timestamp: time.Now().Add(time.Duration(-1 * time.Minute)),
Price: 1.0
}
}, nil)
actual, err := calculator.PriceIncrease()
suite.Equal(100.00, actual)
suite.Nil(err)
}
func (suite *UnitTestSuit) TestCalculate_Error() {
priceProviderMock mocks.PriceProvider{}
calculator := NewPriceIncreaseCalculator(priceProviderMock)
expectedError := errors.New("oh my god")
priceProviderMock.On("List", mock.Anything).Return([]*stocks.PriceData{}, expectedError)
actual, err := calculator.PriceIncrease()
suite.Equal(0.0, actual)
suite.EqualError(err, expectedError)
}
This package provides functionality that you might be used to from more commom OO languages.
With it, you can build a testing suite as a struct, build setup/teardown methods and testing methods on your struct, and run them with go test
as per normal.
For instance, create a complete test with lifecycles, separating unit
tests from integration
tests by putting them into different suites.
Member functions of the test suite, should start with the Test
prefix, or they will NOT be executed.
import "github.com/stretchr/testify/suite"
type ExampleSuite struct {
suite.Suite
VariableAvailableToSuite int
}
// In order for 'go test' to run this suite, we need to create
// a normal test function and pass our suite to suite.Run
func TestExampleSuite(t *testing.T) {
suite.Run(t, &ExampleSuite{})
}
// Will run before EACH test, and set the variable to "5"
func (suite *ExampleSuite) SetupTest() {
suite.VariableAvailableToSuite = 5
}
func (suite *ExampleSuite) TearDownTest() {
suite.T().Log("Test finishes")
}
// All methods that begin with "Test" are run as tests within a suite
func (suite *ExampleSuite) TestExample() {
// If using `assert` you must pass the `testing.T`
assert.Equal(suite.T(), 5, suite.VariableAvailableToSuite)
// Or call assert directly from suite
suite.Equal(5, suite.VariableAvailableToSuite)
}
Lifecycle methods
Here the lifecycle methods in the order they are called:
SetupSuite()
: Executes before all the tests in the suite are run.SetupTest()
: Executes before each tests starts.BeforeTest(suiteName, testName string)
: Execute right before each test starts.AfterTest(suiteName, testName string)
: Execute right after the each test finishes.TearDownTest()
: Executes after each test in the suite.TearDownSuite()
: Executes after all the tests in the suite have been run.
Example
import "github.com/stretchr/testify/suite"
type IntegrationTestSuite struct {
suite.Suite
}
func TestIntegrationTestSuite(t *testing.T) {
suite.Run(t, &IntegrationTestSuite{})
}
func (suite *IntegrationTestSuite) SetupSuite() {
db, err := sql.Open("postgres", ...)
if err != nil {
// FailNow will shutdown gracefully the test suite
suite.FailNowf("unable to connect to database", err.Error())
}
suite.db = db
setupDatabase(suite)
calculator := NewPriceIncreaseCalculator()
suite.calculator = calculator
}
func (suite *IntegrationTestSuite) BeforeTest(suiteName, testName string) {
if testNAme == "TestCalclate_Error" {
return
}
seedTestTable(suite)
}
func (suite *IntegrationTestSuite) TearDownTest() {
cleanTable(suite)
}
func (suite *IntegrationTestSuite) TearDownSuite() {
teardownDatabase(suite)
}
func (suite *IntegrationTestSuite) TestCalculate_Error() {
actual, err := suite.calculator.PriceIncrease()
suite.EqualError(err, "not enough data")
suite.Equal(0.0, actual)
}
func (suite *IntegrationTestSuite) TestCalculate() {
actual, err := suite.calculator.PriceIncrease()
suite.Nil(err)
suite.Equal(100.00, actual)
}
func setupDatabase(suite *IntegrationTestSuite) {}
func seedTestTable(suite *IntegrationTestSuite) {}
func cleanTable(suite *IntegrationTestSuite) {}
func teardownDatabase(suite *IntegrationTestSuite) {}
Last updated