Cosmos SDK 提供了一种分层测试方法,与框架本身的架构相对应。测试被组织为三个层级,每一层都会覆盖应用程序中逐步扩大的一个切面。由于更完整的 counter 模块包含这些示例所需的测试接口,因此本页使用的是 example 仓库中的 counter 模块示例,而不是最小化的 counter 模块示例。 本页中的示例来自 完整 Counter 模块演练 和 运行与测试 教程。

三个测试层级

Keeper 单元测试

Keeper 单元测试会在不启动完整应用程序的情况下,隔离验证 keeper 逻辑。它们会构造一个最小的内存上下文,配合真实的 KV store,初始化待测 keeper,并直接调用其方法。不需要服务器、不需要网络,也不需要区块处理。 counter 模块的 keeper 测试位于 x/counter/keeper/keeper_test.go。测试套件会创建一个带有真实 store 和 mock 依赖的 keeper:
type KeeperTestSuite struct {
	suite.Suite

	ctx         sdk.Context
	keeper      *keeper.Keeper
	queryClient types.QueryClient
	msgServer   types.MsgServer
	bankKeeper  *MockBankKeeper
	authority   string
}

func (s *KeeperTestSuite) SetupTest() {
	key := storetypes.NewKVStoreKey("counter")
	storeService := runtime.NewKVStoreService(key)
	testCtx := testutil.DefaultContextWithDB(s.T(), key, storetypes.NewTransientStoreKey("transient_test"))
	ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Time: cmttime.Now()})
	encCfg := moduletestutil.MakeTestEncodingConfig()

	s.authority = "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn"
	s.bankKeeper = &MockBankKeeper{}
	k := keeper.NewKeeper(storeService, encCfg.Codec, s.bankKeeper, keeper.WithAuthority(s.authority))

	s.ctx = ctx
	s.keeper = k

	queryHelper := baseapp.NewQueryServerTestHelper(ctx, encCfg.InterfaceRegistry)
	types.RegisterQueryServer(queryHelper, keeper.NewQueryServer(k))
	s.queryClient = types.NewQueryClient(queryHelper)
	s.msgServer = keeper.NewMsgServerImpl(k)
}
testutil.DefaultContextWithDB 会创建一个由内存数据库支持的真实 KV store。moduletestutil.MakeTestEncodingConfig 会返回一个为测试配置好的 codec。MockBankKeeper 会替换真实的 bank keeper,使用一个其行为可按测试用例控制的结构体:
type MockBankKeeper struct {
	SendCoinsFromAccountToModuleFn func(ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
}
一个典型的 keeper 测试用例会通过表驱动测试覆盖正常路径和错误条件:
func (s *KeeperTestSuite) TestAddCount() {
	testCases := []struct {
		name         string
		setup        func()
		sender       string
		amount       uint64
		expErr       bool
		expErrMsg    string
		expPostCount uint64
	}{
		{
			name: "add to zero counter",
			setup: func() {
				err := s.keeper.InitGenesis(s.ctx, &types.GenesisState{
					Count:  0,
					Params: types.Params{MaxAddValue: 100},
				})
				s.Require().NoError(err)
			},
			sender:       "cosmos1test",
			amount:       10,
			expErr:       false,
			expPostCount: 10,
		},
		{
			name: "add exceeds max_add_value - should error",
			setup: func() {
				err := s.keeper.InitGenesis(s.ctx, &types.GenesisState{
					Count:  0,
					Params: types.Params{MaxAddValue: 50},
				})
				s.Require().NoError(err)
			},
			sender:    "cosmos1test",
			amount:    100,
			expErr:    true,
			expErrMsg: "exceeds max allowed",
		},
	}

	for _, tc := range testCases {
		s.Run(tc.name, func() {
			s.SetupTest()
			tc.setup()

			newCount, err := s.keeper.AddCount(s.ctx, tc.sender, tc.amount)
			if tc.expErr {
				s.Require().Error(err)
				if tc.expErrMsg != "" {
					s.Require().Contains(err.Error(), tc.expErrMsg)
				}
			} else {
				s.Require().NoError(err)
				s.Require().Equal(tc.expPostCount, newCount)

				count, err := s.keeper.GetCount(s.ctx)
				s.Require().NoError(err)
				s.Require().Equal(tc.expPostCount, count)
			}
		})
	}
}
msg_server_test.go 使用同一个测试套件来测试 MsgServer 层,包括事件发出:
func (s *KeeperTestSuite) TestMsgAddEmitsEvent() {
	s.SetupTest()
	err := s.keeper.InitGenesis(s.ctx, &types.GenesisState{
		Count:  0,
		Params: types.Params{MaxAddValue: 100},
	})
	s.Require().NoError(err)

	_, err = s.msgServer.Add(s.ctx, &types.MsgAddRequest{Sender: "cosmos1test", Add: 42})
	s.Require().NoError(err)

	events := s.ctx.EventManager().Events()
	s.Require().NotEmpty(events)

	found := false
	for _, event := range events {
		if event.Type == "count_increased" {
			found = true
		}
	}
	s.Require().True(found, "count_increased event not found")
}
Keeper 单元测试速度快、结果确定、定位精确。它们非常适合用来测试业务逻辑、错误条件、边界情况以及事件发出。

集成测试

集成测试会验证整个应用栈上的行为。它们会启动一个真实的内存网络,包含一个或多个验证者,等待区块产出,通过 gRPC 广播真实签名交易,并查询最终状态。这些测试会一并覆盖 AnteHandler、消息路由、区块执行和状态提交。 counter 模块的集成测试位于 tests/counter_test.go。测试套件使用 Cosmos SDK 的 testutil/network 启动一条完整的内存链:
type E2ETestSuite struct {
	suite.Suite

	cfg     network.Config
	network *network.Network
	conn    *grpc.ClientConn
}

func (s *E2ETestSuite) SetupSuite() {
	s.T().Log("setting up e2e test suite")

	var err error
	s.cfg = network.DefaultConfig(NewTestNetworkFixture)
	s.cfg.NumValidators = 1

	// Customize counter genesis to set initial count and permissive params
	genesisState := s.cfg.GenesisState
	counterGenesis := countertypes.GenesisState{
		Count: 0,
		Params: countertypes.Params{
			MaxAddValue: 1000,
			AddCost:     nil,
		},
	}
	counterGenesisBz, err := s.cfg.Codec.MarshalJSON(&counterGenesis)
	s.Require().NoError(err)
	genesisState[countertypes.ModuleName] = counterGenesisBz
	s.cfg.GenesisState = genesisState

	s.network, err = network.New(s.T(), s.T().TempDir(), s.cfg)
	s.Require().NoError(err)

	_, err = s.network.WaitForHeight(2)
	s.Require().NoError(err)

	val0 := s.network.Validators[0]
	s.conn, err = grpc.NewClient(
		val0.AppConfig.GRPC.Address,
		grpc.WithTransportCredentials(insecure.NewCredentials()),
		grpc.WithDefaultCallOptions(grpc.ForceCodec(codec.NewProtoCodec(s.cfg.InterfaceRegistry).GRPCCodec())),
	)
	s.Require().NoError(err)
}
NewTestNetworkFixture(位于 tests/test_helpers.go)会使用 dbm.NewMemDB() 构造 ExampleApp,并返回一个用于配置内存验证者的 network.TestFixture。这样 SDK 的网络测试辅助工具就可以启动一个带有真实共识的真实应用。 一个覆盖完整交易路径的测试:
func (s *E2ETestSuite) TestAddCounter() {
	val := s.network.Validators[0]

	initialCount := s.getCurrentCount()

	txBuilder := s.mkCounterAddTx(val, 42)
	txBytes, err := val.ClientCtx.TxConfig.TxEncoder()(txBuilder.GetTx())
	s.Require().NoError(err)

	txClient := txtypes.NewServiceClient(s.conn)
	grpcRes, err := txClient.BroadcastTx(
		context.Background(),
		&txtypes.BroadcastTxRequest{
			Mode:    txtypes.BroadcastMode_BROADCAST_MODE_SYNC,
			TxBytes: txBytes,
		},
	)
	s.Require().NoError(err)
	s.Require().Equal(uint32(0), grpcRes.TxResponse.Code, "tx failed: %s", grpcRes.TxResponse.RawLog)

	s.Require().NoError(s.network.WaitForNextBlock())

	finalCount := s.getCurrentCount()
	s.Require().Equal(initialCount+42, finalCount)
}
集成测试比 keeper 单元测试更慢,因为它们会启动真实的共识引擎并等待出块。它们的价值在于捕获边界上的失败:AnteHandler 拒绝、路由错误、genesis 状态不匹配,以及只有在完整栈运行时才会显现的跨模块交互问题。

模拟测试

模拟测试是基于性质的测试。它们不是测试特定输入,而是生成大量随机操作,并验证应用程序的不变量在整个过程中始终成立。它们能够发现确定性测试用例遗漏的缺陷:意外的顺序效应、高负载下的状态损坏,以及只有在大量连续操作后才会出现的不变量违规。 Cosmos SDK 通过 simsx 提供这一能力。counter 模块定义了一个消息工厂,用来生成随机的 MsgAddRequest 消息:
// x/counter/simulation/msg_factory.go

func MsgAddFactory() simsx.SimMsgFactoryFn[*types.MsgAddRequest] {
	return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *types.MsgAddRequest) {
		sender := testData.AnyAccount(reporter)
		if reporter.IsSkipped() {
			return nil, nil
		}

		r := testData.Rand()
		addAmount := uint64(r.Intn(100) + 1)

		msg := &types.MsgAddRequest{
			Sender: sender.AddressBech32,
			Add:    addAmount,
		}

		return []simsx.SimAccount{sender}, msg
	}
}
模拟运行器会选择一个随机账户和一个处于有效范围内的随机增加值,然后在实时应用上执行该消息。这个过程会在模拟区块序列中重复数千次。 sim_test.go 中的顶层模拟测试会将这些部分连接起来:
//go:build sims

func TestFullAppSimulation(t *testing.T) {
	simsx.Run(t, NewExampleApp, setupStateFactory)
}

func setupStateFactory(app *ExampleApp) simsx.SimStateFactory {
	return simsx.SimStateFactory{
		Codec:         app.AppCodec(),
		AppStateFn:    simtestutil.AppStateFn(app.AppCodec(), app.SimulationManager(), app.DefaultGenesis()),
		BlockedAddr:   BlockedAddresses(),
		AccountSource: app.AccountKeeper,
		BalanceSource: app.BankKeeper,
	}
}
//go:build sims 构建标签意味着模拟测试不会包含在常规的 go test 运行中,只有在显式传入 -tags sims 时才会执行。这样可以保持 CI 足够快速。 模拟管理器会在 app.go 中初始化:
overrideModules := map[string]module.AppModuleSimulation{
	authtypes.ModuleName: auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, nil),
}
app.sm = module.NewSimulationManagerFromAppModules(app.ModuleManager.Modules, overrideModules)
app.sm.RegisterStoreDecoders()
NewSimulationManagerFromAppModules 会收集所有实现了 AppModuleSimulation 的模块所提供的模拟支持。RegisterStoreDecoders 会为每个模块的 store 条目注册可读的解码器,供模拟框架在记录状态以便调试时使用。

测试工具

testutil

testutil 包提供了用于为单元测试构造内存上下文的辅助工具:
  • testutil.DefaultContextWithDB 会创建一个由内存 KV store 支持的真实 sdk.Context。Keeper 单元测试使用它来获得一个真实的执行上下文,而无需启动完整节点。
  • moduletestutil.MakeTestEncodingConfig 会返回一个带有标准接口注册的 codec,适用于 keeper 测试。
  • baseapp.NewQueryServerTestHelper 会创建一个 QueryServiceTestHelper,它同时实现 gRPC Server 和 ClientConn 接口,使 keeper 测试能够注册查询服务并直接调用它们,而无需网络连接。

testify suite

SDK 的测试文件使用 testify/suite 包。suite.Suite 会将测试初始化、清理以及测试方法组织到同一个结构体中。SetupTest 会在每个测试方法之前运行;SetupSuite 会在该 suite 中的所有测试开始前仅运行一次。
func TestKeeperTestSuite(t *testing.T) {
	suite.Run(t, new(KeeperTestSuite))
}
suite.Run 会发现结构体上名称以 Test 开头的方法,并将它们作为独立测试用例运行。s.Require() 会返回断言辅助方法,在断言失败时立即停止测试;而 s.Assert() 会在失败后继续执行。

simsx 和 simd

关于如何配置和运行模拟测试的完整指南,请参阅模块模拟测试页面。 simsx 是模拟执行框架。它提供:
  • SimMsgFactoryFn:一种函数类型,为消息工厂实现 SimMsgFactoryX 接口。每个工厂都会随机选择账户和参数,构造一条消息,并返回它以供执行。
  • ChainDataSource:在构造消息期间提供对随机账户、余额以及其他链上数据的访问。
  • SimulationReporter:允许工厂发出应跳过自身的信号(例如不存在合适账户时)。
  • simsx.Run:顶层入口,用于驱动针对应用程序的一次完整模拟运行。
simd 是 Cosmos SDK 提供的参考模拟二进制。它是一个已完整配置的 simapp(simapp),并编译为独立二进制文件,用于在无需搭建自定义链的情况下,针对 SDK 自身的模块集运行模拟测试。对于示例应用这类自定义链,你需要使用带有 sims 构建标签的自有二进制。要了解如何运行示例链,请参阅 simd 节点教程。 要针对示例应用运行模拟测试:
go test -tags sims -run TestFullAppSimulation ./...
要为你自己的模块添加模拟测试支持,包括实现 AppModuleSimulation、编写消息工厂以及接入 SimulationManager,请参阅模块模拟测试。

Telemetry

counter 模块使用 OpenTelemetry 从 keeper 操作中发出指标:
var (
	meter = otel.Meter("github.com/cosmos/example/x/counter")

	countMetric metric.Int64Counter
)
SDK 提供了一个基于 OpenTelemetry 构建的 telemetry 包,并保留了对 go-metrics 的兼容支持。模块可以在 keeper 方法中发出计数器、仪表和直方图,以暴露运行时行为供监控使用。完整细节请参阅 Telemetry。
The Cosmos SDK provides a layered testing approach that mirrors the architecture of the framework itself. Tests are organized into three levels, each testing a progressively larger slice of the application. This page uses the counter module example in the example repo, not the minimal counter module example, because the fuller module includes the testing surfaces needed for these examples. The examples on this page come from the Full Counter Module Walkthrough and Running and Testing tutorials.

Three testing levels

Keeper unit tests

Keeper unit tests verify keeper logic in isolation, without starting a full application. They construct a minimal in-memory context with a real KV store, initialize the keeper under test, and call its methods directly. No server, no network, no block processing. The counter module keeper tests live in x/counter/keeper/keeper_test.go. The test suite sets up a keeper with a live store and mock dependencies:
type KeeperTestSuite struct {
	suite.Suite

	ctx         sdk.Context
	keeper      *keeper.Keeper
	queryClient types.QueryClient
	msgServer   types.MsgServer
	bankKeeper  *MockBankKeeper
	authority   string
}

func (s *KeeperTestSuite) SetupTest() {
	key := storetypes.NewKVStoreKey("counter")
	storeService := runtime.NewKVStoreService(key)
	testCtx := testutil.DefaultContextWithDB(s.T(), key, storetypes.NewTransientStoreKey("transient_test"))
	ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Time: cmttime.Now()})
	encCfg := moduletestutil.MakeTestEncodingConfig()

	s.authority = "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn"
	s.bankKeeper = &MockBankKeeper{}
	k := keeper.NewKeeper(storeService, encCfg.Codec, s.bankKeeper, keeper.WithAuthority(s.authority))

	s.ctx = ctx
	s.keeper = k

	queryHelper := baseapp.NewQueryServerTestHelper(ctx, encCfg.InterfaceRegistry)
	types.RegisterQueryServer(queryHelper, keeper.NewQueryServer(k))
	s.queryClient = types.NewQueryClient(queryHelper)
	s.msgServer = keeper.NewMsgServerImpl(k)
}
testutil.DefaultContextWithDB creates a real KV store backed by an in-memory database. moduletestutil.MakeTestEncodingConfig returns a codec configured for the test. The MockBankKeeper replaces the real bank keeper with a struct whose behavior can be controlled per test case:
type MockBankKeeper struct {
	SendCoinsFromAccountToModuleFn func(ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
}
A typical keeper test case covers the happy path and the error conditions with table-driven tests:
func (s *KeeperTestSuite) TestAddCount() {
	testCases := []struct {
		name         string
		setup        func()
		sender       string
		amount       uint64
		expErr       bool
		expErrMsg    string
		expPostCount uint64
	}{
		{
			name: "add to zero counter",
			setup: func() {
				err := s.keeper.InitGenesis(s.ctx, &types.GenesisState{
					Count:  0,
					Params: types.Params{MaxAddValue: 100},
				})
				s.Require().NoError(err)
			},
			sender:       "cosmos1test",
			amount:       10,
			expErr:       false,
			expPostCount: 10,
		},
		{
			name: "add exceeds max_add_value - should error",
			setup: func() {
				err := s.keeper.InitGenesis(s.ctx, &types.GenesisState{
					Count:  0,
					Params: types.Params{MaxAddValue: 50},
				})
				s.Require().NoError(err)
			},
			sender:    "cosmos1test",
			amount:    100,
			expErr:    true,
			expErrMsg: "exceeds max allowed",
		},
	}

	for _, tc := range testCases {
		s.Run(tc.name, func() {
			s.SetupTest()
			tc.setup()

			newCount, err := s.keeper.AddCount(s.ctx, tc.sender, tc.amount)
			if tc.expErr {
				s.Require().Error(err)
				if tc.expErrMsg != "" {
					s.Require().Contains(err.Error(), tc.expErrMsg)
				}
			} else {
				s.Require().NoError(err)
				s.Require().Equal(tc.expPostCount, newCount)

				count, err := s.keeper.GetCount(s.ctx)
				s.Require().NoError(err)
				s.Require().Equal(tc.expPostCount, count)
			}
		})
	}
}
msg_server_test.go uses the same suite to test the MsgServer layer, including event emission:
func (s *KeeperTestSuite) TestMsgAddEmitsEvent() {
	s.SetupTest()
	err := s.keeper.InitGenesis(s.ctx, &types.GenesisState{
		Count:  0,
		Params: types.Params{MaxAddValue: 100},
	})
	s.Require().NoError(err)

	_, err = s.msgServer.Add(s.ctx, &types.MsgAddRequest{Sender: "cosmos1test", Add: 42})
	s.Require().NoError(err)

	events := s.ctx.EventManager().Events()
	s.Require().NotEmpty(events)

	found := false
	for _, event := range events {
		if event.Type == "count_increased" {
			found = true
		}
	}
	s.Require().True(found, "count_increased event not found")
}
Keeper unit tests are fast, deterministic, and surgical. They are the right level for testing business logic, error conditions, edge cases, and event emission.

Integration tests

Integration tests verify behavior across the full application stack. They start a real in-memory network with one or more validators, wait for blocks to be produced, broadcast actual signed transactions via gRPC, and query the resulting state. These tests exercise the AnteHandler, message routing, block execution, and state commitment together. The counter module integration tests live in tests/counter_test.go. The test suite uses testutil/network from the Cosmos SDK to spin up a full in-memory chain:
type E2ETestSuite struct {
	suite.Suite

	cfg     network.Config
	network *network.Network
	conn    *grpc.ClientConn
}

func (s *E2ETestSuite) SetupSuite() {
	s.T().Log("setting up e2e test suite")

	var err error
	s.cfg = network.DefaultConfig(NewTestNetworkFixture)
	s.cfg.NumValidators = 1

	// Customize counter genesis to set initial count and permissive params
	genesisState := s.cfg.GenesisState
	counterGenesis := countertypes.GenesisState{
		Count: 0,
		Params: countertypes.Params{
			MaxAddValue: 1000,
			AddCost:     nil,
		},
	}
	counterGenesisBz, err := s.cfg.Codec.MarshalJSON(&counterGenesis)
	s.Require().NoError(err)
	genesisState[countertypes.ModuleName] = counterGenesisBz
	s.cfg.GenesisState = genesisState

	s.network, err = network.New(s.T(), s.T().TempDir(), s.cfg)
	s.Require().NoError(err)

	_, err = s.network.WaitForHeight(2)
	s.Require().NoError(err)

	val0 := s.network.Validators[0]
	s.conn, err = grpc.NewClient(
		val0.AppConfig.GRPC.Address,
		grpc.WithTransportCredentials(insecure.NewCredentials()),
		grpc.WithDefaultCallOptions(grpc.ForceCodec(codec.NewProtoCodec(s.cfg.InterfaceRegistry).GRPCCodec())),
	)
	s.Require().NoError(err)
}
NewTestNetworkFixture (in tests/test_helpers.go) constructs the ExampleApp with dbm.NewMemDB() and returns a network.TestFixture that configures the in-memory validator. This lets the SDK’s network test helper start a real application with real consensus. A test that exercises the full transaction path:
func (s *E2ETestSuite) TestAddCounter() {
	val := s.network.Validators[0]

	initialCount := s.getCurrentCount()

	txBuilder := s.mkCounterAddTx(val, 42)
	txBytes, err := val.ClientCtx.TxConfig.TxEncoder()(txBuilder.GetTx())
	s.Require().NoError(err)

	txClient := txtypes.NewServiceClient(s.conn)
	grpcRes, err := txClient.BroadcastTx(
		context.Background(),
		&txtypes.BroadcastTxRequest{
			Mode:    txtypes.BroadcastMode_BROADCAST_MODE_SYNC,
			TxBytes: txBytes,
		},
	)
	s.Require().NoError(err)
	s.Require().Equal(uint32(0), grpcRes.TxResponse.Code, "tx failed: %s", grpcRes.TxResponse.RawLog)

	s.Require().NoError(s.network.WaitForNextBlock())

	finalCount := s.getCurrentCount()
	s.Require().Equal(initialCount+42, finalCount)
}
Integration tests are slower than keeper unit tests because they start a real consensus engine and wait for blocks. They exist to catch failures at the boundaries: AnteHandler rejections, routing errors, genesis state mismatches, and cross-module interactions that only manifest when the full stack is running.

Simulation tests

Simulation tests are property-based tests. Instead of testing specific inputs, they generate large volumes of random operations and verify that the application’s invariants hold throughout. They catch bugs that deterministic test cases miss: unexpected ordering effects, state corruption under high load, and invariant violations that only appear after many sequential operations. The Cosmos SDK simulation framework drives this through simsx. The counter module defines a message factory that generates random MsgAddRequest messages:
// x/counter/simulation/msg_factory.go

func MsgAddFactory() simsx.SimMsgFactoryFn[*types.MsgAddRequest] {
	return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *types.MsgAddRequest) {
		sender := testData.AnyAccount(reporter)
		if reporter.IsSkipped() {
			return nil, nil
		}

		r := testData.Rand()
		addAmount := uint64(r.Intn(100) + 1)

		msg := &types.MsgAddRequest{
			Sender: sender.AddressBech32,
			Add:    addAmount,
		}

		return []simsx.SimAccount{sender}, msg
	}
}
The simulation runner selects a random account and a random add amount within valid bounds, then executes the message against the live application. This runs thousands of times across a simulated block sequence. The top-level simulation test in sim_test.go wires everything together:
//go:build sims

func TestFullAppSimulation(t *testing.T) {
	simsx.Run(t, NewExampleApp, setupStateFactory)
}

func setupStateFactory(app *ExampleApp) simsx.SimStateFactory {
	return simsx.SimStateFactory{
		Codec:         app.AppCodec(),
		AppStateFn:    simtestutil.AppStateFn(app.AppCodec(), app.SimulationManager(), app.DefaultGenesis()),
		BlockedAddr:   BlockedAddresses(),
		AccountSource: app.AccountKeeper,
		BalanceSource: app.BankKeeper,
	}
}
The //go:build sims build tag means simulation tests are excluded from regular go test runs and only execute when explicitly requested with -tags sims. This keeps CI fast. The simulation manager is initialized in app.go:
overrideModules := map[string]module.AppModuleSimulation{
	authtypes.ModuleName: auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, nil),
}
app.sm = module.NewSimulationManagerFromAppModules(app.ModuleManager.Modules, overrideModules)
app.sm.RegisterStoreDecoders()
NewSimulationManagerFromAppModules collects simulation support from all modules that implement AppModuleSimulation. RegisterStoreDecoders registers human-readable decoders for each module’s store entries, used when the simulation framework logs state for debugging.

Test utilities

testutil

The testutil package provides helpers for constructing in-memory contexts for unit tests:
  • testutil.DefaultContextWithDB creates a real sdk.Context backed by an in-memory KV store. Keeper unit tests use this to get a realistic execution context without starting a full node.
  • moduletestutil.MakeTestEncodingConfig returns a codec with standard interface registration, suitable for keeper tests.
  • baseapp.NewQueryServerTestHelper creates a QueryServiceTestHelper that implements both the gRPC Server and ClientConn interfaces, allowing keeper tests to register query services and invoke them directly without a network connection.

testify suite

The SDK’s test files use the testify/suite package. A suite.Suite groups test setup, teardown, and test methods into a single struct. SetupTest runs before each test method; SetupSuite runs once before all tests in the suite.
func TestKeeperTestSuite(t *testing.T) {
	suite.Run(t, new(KeeperTestSuite))
}
suite.Run discovers methods on the struct whose names start with Test and runs them as individual test cases. s.Require() returns assertion helpers that stop the test immediately on failure, while s.Assert() continues after a failure.

simsx and simd

For a full guide on configuring and running simulations, see the Module Simulation page. simsx is the simulation execution framework. It provides:
  • SimMsgFactoryFn: a function type that implements the SimMsgFactoryX interface for message factories. Each factory selects random accounts and parameters, constructs a message, and returns it for execution.
  • ChainDataSource: provides access to random accounts, balances, and other chain data during message construction.
  • SimulationReporter: allows a factory to signal that it should be skipped (for example, if no suitable account exists).
  • simsx.Run: the top-level entry point that drives a full simulation run against the application.
simd is the reference simulation binary provided by the Cosmos SDK. It is a fully configured simapp (simapp) compiled as a standalone binary, used to run simulations against the SDK’s own module set without setting up a custom chain. For a custom chain like the example app, you use your own binary with the sims build tag. To learn how to run an example chain, visit the simd node tutorial. To run simulations against the example app:
go test -tags sims -run TestFullAppSimulation ./...
To add simulation support to your own module — implementing AppModuleSimulation, writing message factories, and wiring the SimulationManager — see Module Simulation.

Telemetry

The counter module uses OpenTelemetry to emit metrics from keeper operations:
var (
	meter = otel.Meter("github.com/cosmos/example/x/counter")

	countMetric metric.Int64Counter
)
The SDK provides a telemetry package built around OpenTelemetry, with legacy support for go-metrics. Modules can emit counters, gauges, and histograms from keeper methods to expose runtime behavior for monitoring. See Telemetry for full details.