Testify Suites中SetupSuite与SetupTest的区别、示例及依赖注入差异
SetupSuite vs SetupTest - Practical Examples & Dependency Injection Differences Great question! Let's break this down with concrete practical examples and clear distinctions around dependency injection for these two Testify lifecycle methods.
Practical Application Examples
When to Use SetupSuite
SetupSuite runs once before all tests in the suite start, making it perfect for initializing heavy, shared resources that don't need to be reset between tests.
Example 1: Global Database Connection Pool
If your tests interact with a database, initializing a connection pool once (instead of per test) saves overhead and keeps your tests fast:
import ( "database/sql" "github.com/stretchr/testify/suite" "github.com/stretchr/testify/require" _ "github.com/lib/pq" ) type DatabaseTestSuite struct { suite.Suite dbPool *sql.DB } func (suite *DatabaseTestSuite) SetupSuite() { // Initialize connection pool once for the entire suite var err error suite.dbPool, err = sql.Open("postgres", "postgres://test:test@localhost/test_db?sslmode=disable") require.NoError(suite.T(), err) // Verify connection err = suite.dbPool.Ping() require.NoError(suite.T(), err) } // All tests reuse the same dbPool instance func (suite *DatabaseTestSuite) TestCreateRecord() { _, err := suite.dbPool.Exec("INSERT INTO items (name) VALUES ($1)", "TestItem") suite.NoError(err) } func (suite *DatabaseTestSuite) TestFetchRecord() { var name string err := suite.dbPool.QueryRow("SELECT name FROM items WHERE name = $1", "TestItem").Scan(&name) suite.NoError(err) suite.Equal("TestItem", name) }
Example 2: Load Global Configuration
If all tests rely on the same config values (API endpoints, environment settings), load them once in SetupSuite:
type AppConfig struct { APIBaseURL string Timeout int } func LoadConfig(path string) (AppConfig, error) { // Logic to load from YAML/env vars return AppConfig{APIBaseURL: "https://api.test.com", Timeout: 10}, nil } type APITestSuite struct { suite.Suite config AppConfig } func (suite *APITestSuite) SetupSuite() { var err error suite.config, err = LoadConfig("test_config.yaml") require.NoError(suite.T(), err) } func (suite *APITestSuite) TestAPIRequest() { // Use suite.config.APIBaseURL for all API calls in tests client := &http.Client{Timeout: time.Duration(suite.config.Timeout) * time.Second} _, err := client.Get(suite.config.APIBaseURL + "/health") suite.NoError(err) }
When to Use SetupTest
SetupTest runs before every individual test case, making it ideal for ensuring test isolation by resetting state or initializing test-specific resources.
Example 1: Reset Test Data
Keep tests independent by cleaning up or seeding fresh data before each test:
type IsolatedDatabaseSuite struct { suite.Suite dbPool *sql.DB } func (suite *IsolatedDatabaseSuite) SetupSuite() { // Initialize dbPool once (same as before) var err error suite.dbPool, err = sql.Open("postgres", "postgres://test:test@localhost/test_db?sslmode=disable") require.NoError(suite.T(), err) } func (suite *IsolatedDatabaseSuite) SetupTest() { // Truncate tables and seed fresh test data before every test _, err := suite.dbPool.Exec("TRUNCATE TABLE users RESTART IDENTITY") require.NoError(suite.T(), err) _, err = suite.dbPool.Exec("INSERT INTO users (id, name) VALUES (1, 'TestUser')") require.NoError(suite.T(), err) } func (suite *IsolatedDatabaseSuite) TestUpdateUser() { // This test starts with a fresh TestUser record _, err := suite.dbPool.Exec("UPDATE users SET name = $1 WHERE id = $2", "UpdatedUser", 1) suite.NoError(err) var name string err = suite.dbPool.QueryRow("SELECT name FROM users WHERE id = $1", 1).Scan(&name) suite.NoError(err) suite.Equal("UpdatedUser", name) } func (suite *IsolatedDatabaseSuite) TestDeleteUser() { // This test also starts with the same fresh TestUser, no leftover data from the previous test _, err := suite.dbPool.Exec("DELETE FROM users WHERE id = $1", 1) suite.NoError(err) var count int err = suite.dbPool.QueryRow("SELECT COUNT(*) FROM users").Scan(&count) suite.NoError(err) suite.Equal(0, count) }
Example 2: Test-Specific Mock Dependencies
When using mocks, create a new instance per test to avoid cross-test interference:
import "github.com/stretchr/testify/mock" type EmailClient interface { Send(to, subject string) error } type MockEmailClient struct { mock.Mock } func (m *MockEmailClient) Send(to, subject string) error { args := m.Called(to, subject) return args.Error(0) } type UserServiceSuite struct { suite.Suite userService *UserService emailMock *MockEmailClient } func (suite *UserServiceSuite) SetupTest() { // Create a new mock for every test suite.emailMock = &MockEmailClient{} suite.userService = &UserService{EmailClient: suite.emailMock} } func (suite *UserServiceSuite) TestSendWelcomeEmail() { // Set expectations for THIS test only suite.emailMock.On("Send", "user@example.com", "Welcome").Return(nil) err := suite.userService.SendWelcomeEmail("user@example.com") suite.NoError(err) suite.emailMock.AssertExpectations(suite.T()) } func (suite *UserServiceSuite) TestSendPasswordReset() { // Different expectations for this test, no overlap with the previous one suite.emailMock.On("Send", "user@example.com", "Password Reset").Return(nil) err := suite.userService.SendPasswordReset("user@example.com") suite.NoError(err) suite.emailMock.AssertExpectations(suite.T()) }
Dependency Injection Differences
| Aspect | SetupSuite Injection | SetupTest Injection |
|---|---|---|
| Lifecycle | Injected resources exist for the entire suite. | Injected resources are recreated per test. |
| Use Case | Shared, thread-safe, stateless resources (connection pools, configs). | Test-specific, stateful resources (mocks, fresh data). |
| Isolation | Resources are shared across tests—changes affect all tests. | Resources are isolated per test—no cross-test interference. |
| Overhead | Low: initialized once. | Higher: initialized N times (N = number of tests). |
Key Takeaway
- Use
SetupSuitefor dependencies that are expensive to initialize and safe to share across all tests. - Use
SetupTestfor dependencies that need to be reset or customized for each individual test to ensure test isolation.
内容的提问来源于stack exchange,提问作者Mohit Jain

