You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Testify Suites中SetupSuite与SetupTest的区别、示例及依赖注入差异

Testify Suites: 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

AspectSetupSuite InjectionSetupTest Injection
LifecycleInjected resources exist for the entire suite.Injected resources are recreated per test.
Use CaseShared, thread-safe, stateless resources (connection pools, configs).Test-specific, stateful resources (mocks, fresh data).
IsolationResources are shared across tests—changes affect all tests.Resources are isolated per test—no cross-test interference.
OverheadLow: initialized once.Higher: initialized N times (N = number of tests).

Key Takeaway

  • Use SetupSuite for dependencies that are expensive to initialize and safe to share across all tests.
  • Use SetupTest for dependencies that need to be reset or customized for each individual test to ensure test isolation.

内容的提问来源于stack exchange,提问作者Mohit Jain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:45:28