kdocs
GitHub
Lang - Runtime
Lang - Runtime
  • Go
    • Modules & Packages
    • Variables
    • Structs
    • Interfaces
    • Functions
    • Control Structures
    • Erro Handling
    • Concurrency
    • Testing
    • Fuzzing
  • Web
    • V8 Engine
    • Node.js
      • New Project
    • Deno
      • New Project
Powered by GitBook
On this page
  • Run tests
  • Testify
  • Assert
  • Require
  • Mock
  • Suite
  1. Go

Testing

PreviousConcurrencyNextFuzzing

Last updated 1 month ago

Run tests

Search recursively and run the ALL tests:

go test ./...

Running only go test will look for tests only at the current folder.

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.

Using only the native testing package of Go considerably increases boilerplate, and test code, since you will have to manually write errors, and manually fail tests.

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.

mocks/PriceProvider.go
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
}
PriceProvider_test.go
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)
}

The suite package does not support parallel tests.

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

AfterTest and BeforeTest are useful because they have access to the test name being runned, so if you must do additional logic to only specific tests, use them.

  • BeforeTest is called after SetupTest.

  • AfterTest is called before TearDownTest.

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) {}

You can use to auto generate mock functions from Structs.

Testify
Assert
Mock
Mockery
Suite