Skip to content
On this page

End-to-End (E2E) Testing

This document provides an overview of how to perform End-to-End (E2E) testing in Golang using the Testify framework. The code provided demonstrates a sample E2E test for a user service.

Setting up the Test Suite

In this example, we'll use Testify's test suite functionality to define and run our tests. The UserServiceSuite struct embeds suite.Suite from Testify, allowing us to use Testify's testing features.

go
package services

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/suite"
	core "gitlab.finema.co/finema/idin-core"
)

type UserServiceSuite struct {
	suite.Suite
	ctx core.IE2EContext
}

func TestUserServiceSuite(t *testing.T) {
	suite.Run(t, new(UserServiceSuite))
}
  • The UserServiceSuite struct is our test suite that will contain all the individual test cases.
  • The ctx field represents the context for our E2E tests. It implements the core.IE2EContext interface.

The TestUserServiceSuite function is the entry point for running the test suite. It uses suite.Run to execute the test suite within the standard Go testing framework.

Setting up Test Environment

To set up the test environment, we define the SetupSuite and TearDownSuite methods within the UserServiceSuite struct. These methods are called before and after running the entire test suite, respectively.

go
func (suite *UserServiceSuite) SetupSuite() {
	// Set up the test environment (e.g., connect to the database)
	suite.ctx = createE2EContext() // Set up the actual implementation of core.IE2EContext
}

func (suite *UserServiceSuite) TearDownSuite() {
	// Clean up the test environment (e.g., close database connections)
}
  • The SetupSuite method is responsible for setting up the test environment before running the test suite. Here, you can establish database connections or perform any other necessary setup.
  • The TearDownSuite method is used for cleaning up the test environment after running the test suite.

Setting up Individual Test Cases

For each test case, we define the SetupTest and TearDownTest methods within the UserServiceSuite struct. These methods are called before and after each individual test case, respectively.

go
func (suite *UserServiceSuite) SetupTest() {
	// Prepare the test data (e.g., insert test users into the database)
}

func (suite *UserServiceSuite) TearDownTest() {
	// Clean up the test data (e.g., delete test users from the database)
}
  • The SetupTest method prepares the test data required for the specific test case. Here, you can insert test users into the database or perform any other necessary setup steps.
  • The TearDownTest method cleans up the test data after the test case has run.

Creating the E2E Context

The createE2EContext function is responsible for creating the actual implementation of the core.IE2EContext interface. In this example, we create a mock implementation for testing purposes.

go
func createE2EContext() core.IE2EContext {
	// Create a mock implementation of core.IContext for testing purposes
	// Return the mock implementation
	env := core.NewENVPath(rootDir())
	db, err := core.NewDatabase(env.Config()).Connect()
	if err != nil {
		fmt.Fprintf(os.Stderr, "MySQL: %v", err)
		os.Exit(1)
	}

	return core.NewE2EContext(&core.E2EContextOptions{
		ContextOptions: &core.ContextOptions{
			ENV: env,
			DB:  db,
		},
	})
}
  • The createE2EContext function sets up a mock implementation of the core.IE2EContext interface for testing purposes.
  • It creates an instance of the core.ENVPath using the rootDir function to determine the root directory.
  • It establishes a database connection using the environment configuration and returns the resulting core.IE2EContext implementation.

Helper Functions

The rootDir function is a helper function used by createE2EContext to determine the root directory of the project.

go
func rootDir() string {
	_, b, _, _ := runtime.Caller(0)
	d := path.Join(path.Dir(b))
	return filepath.Dir(d)
}
  • The rootDir function uses the runtime.Caller function to determine the current file's path and extracts the directory portion.
  • It then returns the parent directory of the extracted directory.

Writing Test Cases

In our example, we have a single test case named TestCreate. This test case demonstrates how to test the Create method of the UserService.

go
func (suite *UserServiceSuite) TestCreate() {
	// Prepare the test data
	payload := &UserCreatePayload{
		Email:    "[email protected]",
		FullName: "John Doe",
	}

	svc := NewUserService(suite.ctx)

	// Call the actual service method
	user, ierr := svc.Create(payload)

	// Assert the result
	assert.NoError(suite.T(), ierr)
	assert.NotNil(suite.T(), user)
	assert.Equal(suite.T(), payload.Email, user.Email)
}
  • The TestCreate method is the actual test case that verifies the behavior of the Create method.
  • We prepare the test data by creating a payload object representing the user's details.
  • We create an instance of the UserService using the suite.ctx context.
  • Next, we call the Create method of the service and capture the returned user and any error.
  • Finally, we use Testify's assertions to verify the expected results. In this case, we check that the error is nil, the user is not nil, and the user's email matches the email in the payload.

Running the Tests

To run the E2E tests, execute the following command in your terminal:

go test ./services

This command runs all the tests within the services package.

That's it! You have now created an E2E test suite using Testify in Golang. Feel free to add more test cases or modify the existing ones based on your application's requirements.

Remember to update the import statements in the code according to your project's package structure.

Example

go
package services

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/suite"
	core "gitlab.finema.co/finema/idin-core"
)

type UserServiceSuite struct {
	suite.Suite
	ctx core.IE2EContext
}

func TestUserControllerSuite(t *testing.T) {
	suite.Run(t, new(UserServiceSuite))
}

func (suite *UserServiceSuite) SetupSuite() {
	// Set up the test environment (e.g., connect to the database)
	suite.ctx = createE2EContext() // Set up the actual implementation of core.IE2EContext
}

func (suite *UserServiceSuite) TearDownSuite() {
	// Clean up the test environment (e.g., close database connections)
}

func (suite *UserServiceSuite) SetupTest() {
	// Prepare the test data (e.g., insert test users into the database)
}

func (suite *UserServiceSuite) TearDownTest() {
	// Clean up the test data (e.g., delete test users from the database)
}

func (suite *UserServiceSuite) TestCreate() {
	// Prepare the test data
	payload := &UserCreatePayload{
		Email:    "[email protected]",
		FullName: "John Doe",
	}

	svc := NewUserService(suite.ctx)

	// Call the actual service method
	user, ierr := svc.Create(payload)

	// Assert the result
	assert.NoError(suite.T(), ierr)
	assert.NotNil(suite.T(), user)
	assert.Equal(suite.T(), payload.Email, user.Email)
}

func createE2EContext() core.IE2EContext {
	// Create a mock implementation of core.IContext for testing purposes
	// Return the mock implementation
	env := core.NewENVPath(rootDir())
	db, err := core.NewDatabase(env.Config()).Connect()
	if err != nil {
		fmt.Fprintf(os.Stderr, "MySQL: %v", err)
		os.Exit(1)
	}

	return core.NewE2EContext(&core.E2EContextOptions{
		ContextOptions: &core.ContextOptions{
			ENV: env,
			DB:  db,
		},
	})
}

func rootDir() string {
	_, b, _, _ := runtime.Caller(0)
	d := path.Join(path.Dir(b))
	return filepath.Dir(d)
}

Conclusion

In this document, we have covered the process of performing End-to-End (E2E) testing in Golang using the Testify framework. We explored a sample code that demonstrates an E2E test for a user service.

Here are the key takeaways from this guide:

  • Testify is a powerful testing toolkit for the Go programming language that provides a rich set of assertions and test suite functionality.
  • Test suites in Testify help organize and manage multiple test cases.
  • The SetupSuite and TearDownSuite methods are used to set up and clean up the test environment before and after running the entire test suite.
  • The SetupTest and TearDownTest methods are used to prepare and clean up test data for individual test cases.
  • Test cases are defined as methods within the test suite struct and can be written using Testify's assertions to verify the expected results.
  • Creating a mock implementation of the core.IE2EContext interface allows us to isolate and test the user service's behavior without relying on external dependencies.

By following these guidelines and leveraging the capabilities of Testify, you can effectively perform E2E testing for your Golang applications. E2E tests help ensure that your system functions as expected in real-world scenarios, giving you confidence in the reliability and correctness of your code.

Remember to adapt the provided code to match your specific project's requirements, including adjusting the import statements and modifying the test cases to reflect your application's logic.

Happy testing!

Maintained by Passakon Puttasuwan & Dev Core Team.