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.
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 thecore.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.
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.
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.
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 thecore.IE2EContext
interface for testing purposes. - It creates an instance of the
core.ENVPath
using therootDir
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.
func rootDir() string {
_, b, _, _ := runtime.Caller(0)
d := path.Join(path.Dir(b))
return filepath.Dir(d)
}
- The
rootDir
function uses theruntime.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
.
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 theCreate
method. - We prepare the test data by creating a
payload
object representing the user's details. - We create an instance of the
UserService
using thesuite.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
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
andTearDownSuite
methods are used to set up and clean up the test environment before and after running the entire test suite. - The
SetupTest
andTearDownTest
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!