既然你已经从零构建了一个模块,并且完整学习了计数器模块全流程讲解,下一步就是掌握运行和验证可用于生产环境的链的工作流。本页将展示如何在本地启动链、通过 CLI 与其交互,以及在发布变更前使用哪些主要测试层进行验证。

单节点本地链

在本地开发时,单节点链可以提供最快的迭代循环。它只提供一个验证者,状态可预测,因此你可以快速测试查询和交易。

启动

make start
这会构建二进制文件、初始化链数据,并启动一个单验证者节点。它会自动处理清理工作,因此每次运行时都会重置已有链状态。 该链使用:
  • 链 ID:demo
  • 预注资账户:alice、bob
  • 默认代币单位:stake

停止

在运行 make start 的终端中按 Ctrl+C。

重置链状态

make start
重新运行 make start 会自动重置状态,不需要单独的重置命令。

Localnet(多验证者)

当你需要更接近真实网络的环境时,请使用 localnet。它会在 Docker 中运行多个验证者,以便你在本地测试多节点行为。 使用 Docker 搭建多验证者环境:
# Initialize localnet configuration
make localnet-init

# Start all validators
make localnet-start

# View logs
make localnet-logs

# Stop
make localnet-stop

# Clean all localnet data
make localnet-clean

CLI 参考

链启动后,下面这些核心 CLI 命令可用于检查状态和提交交易。

查询命令

使用查询命令可以读取模块状态,而不会修改链上任何内容。
# Query the current counter value
exampled query counter count

# Query the module parameters
exampled query counter params

# Query with a specific node (if not using default localhost:26657)
exampled query counter count --node tcp://localhost:26657

交易命令

使用交易命令可以向链提交会修改状态的消息。
# Add to the counter
exampled tx counter add 10 --from alice --chain-id demo --yes

# Add with a gas limit
exampled tx counter add 10 --from alice --chain-id demo --gas 200000 --yes

# Update module parameters (requires governance authority)
exampled tx counter update-params --from alice --chain-id demo --yes

常用标志

这些是你在本地迭代时最常使用的标志。
标志说明
--from用于签名的密钥名或地址
--chain-id链 ID(本地使用 demo)
--yes跳过确认提示
--gas交易的 Gas 限额
--nodeRPC 端点(默认:tcp://localhost:26657)
--output json以 JSON 格式输出响应

节点配置

当你运行 make start 时,链会自动创建 ~/.exampleapp/config/,并在其中初始化两个配置文件:
文件控制内容
app.tomlSDK 应用配置:gas price、裁剪、API/gRPC 服务、遥测
config.tomlCometBFT 配置:对等网络、共识超时、mempool、RPC

app.toml

开发过程中最常调整的配置项:
配置项默认值说明
minimum-gas-prices"0stake"节点在处理交易前可接受的最低费用
pruning"default"保留多少历史状态(default、nothing、everything、custom)
api.enabletrue启用 1317 端口上的 REST API
grpc.enabletrue启用 9090 端口上的 gRPC 服务器

config.toml

开发过程中最可能调整的配置项:
配置项默认值说明
moniker"test"节点的人类可读名称
log_level"info"日志详细级别(debug、info、error)
consensus.timeout_commit"5s"一个区块提交后、开始下一个区块前的等待时间
p2p.seeds""在真实网络中用于连接的种子节点
p2p.persistent_peers""需要保持长期连接的对等节点

单元测试

如果你希望在不运行整条链的情况下快速获得模块逻辑反馈,应从这里开始。这些测试将 keeper 和 gRPC 服务器与应用其余部分隔离开来。 单元测试逻辑位于 main 分支上的 counter keeper 包中:共享测试套件初始化在 x/counter/keeper/keeper_test.go,消息路径测试在 x/counter/keeper/msg_server_test.go,查询路径测试在 x/counter/keeper/query_server_test.go。 keeper 测试套件使用内存存储和 mock bank keeper,对 keeper、msg server 和 query server 分别进行隔离测试。不需要运行中的链。
go test ./x/counter/...
如需显示详细输出:
go test -v ./x/counter/...
如需运行某个特定测试:
go test -v -run TestKeeperTestSuite/TestAddCount ./x/counter/...
该测试套件围绕以下三个文件组织:
文件测试内容
keeper/keeper_test.goGenesis、GetCount、AddCount、SetParams
keeper/msg_server_test.goMsgAdd、事件发出、MsgUpdateParams
keeper/query_server_test.goQueryCount、QueryParams

E2E 测试

当你希望针对真实节点验证完整请求路径时,请运行 E2E 测试。它们比单元测试提供更高的信心,但执行时间也更长。 E2E 逻辑位于 main 分支上的 tests/counter_test.go,它会启动一个进程内网络、构建已签名交易并验证查询结果。它使用的共享网络夹具定义在 tests/test_helpers.go 中。 E2E 测试套件会启动一个真实的进程内验证者网络,并向其提交真实交易。这会覆盖完整栈:交易编码、消息路由、keeper 逻辑和查询响应。
go test -v -run TestE2ETestSuite ./tests/...
由于 E2E 测试需要启动真实节点,因此比单元测试更慢。合并重要变更前应运行它们。

模拟测试

模拟测试 会通过随机化活动对链进行压力测试,以捕获定向测试容易遗漏的边界情况。在这个仓库中,这套模拟流程基于 simsx 构建;simsx 是 Cosmos SDK 提供的更高层模拟框架,用于在模块层定义随机链上活动。 main 分支上的顶层模拟测试命令通过 sim_test.go 运行。counter 模块的 simsx 注册位于 x/counter/module.go,随机 MsgAdd 生成逻辑位于 x/counter/simulation/msg_factory.go,随机化 counter genesis 位于 x/counter/simulation/genesis.go。 在实际使用中,simsx 允许每个模块描述三件事:如何生成随机初始状态、模拟过程中可以发生哪些操作,以及每种操作应以多高频率被选中。对于 x/counter,这意味着生成随机初始计数值、将 MsgAdd 注册为模拟操作,并为其分配权重,以便模拟器知道相对于其他模块操作应多频繁地尝试它。 当你运行某个模拟目标时,测试框架会反复构建应用实例、创建随机账户和余额、根据已注册的模块操作生成随机交易,并在多个区块上执行它们。因此,simsx 很适合发现那些难以通过手写测试覆盖的问题,例如状态机缺陷、意外 panic、不变量违规,以及多次运行之间的非确定性行为。 模拟测试会使用随机生成的交易运行链,以检测非确定性和不变量违规。
# Full simulation
make test-sim-full

# Determinism check
make test-sim-determinism

# All simulation tests
make test-sim
模拟测试需要 sims build tag,而 Makefile 目标会自动处理这一点。

Lint

Lint 是在 CI 或代码评审之前,最快发现风格问题和常见代码质量问题的方法。 lint 命令定义在仓库的 Makefile 中,该文件会安装 golangci-lint 并在整个模块树上运行它。
make lint
这会在整个仓库中安装并运行 golangci-lint。如需在可能的情况下自动修复问题:
make lint-fix

测试总结

可以将下表作为快速参考,根据你所做变更的类型选择合适的验证命令。
命令验证内容
go test ./x/counter/...单独验证 Keeper、MsgServer、QueryServer
go test -run TestE2ETestSuite ./tests/...在真实节点上验证完整交易与查询流程
make test-sim-full检查非确定性和不变量违规
make lint代码风格和静态分析

Now that you’ve built a module from scratch and walked through the full counter module, the next step is learning the workflow for running and validating a production-ready chain. This page shows how to start the chain locally, interact with it through the CLI, and use the main layers of testing before shipping changes.

Single-node local chain

Use a single-node chain for the fastest local development loop. It gives you one validator with predictable state so you can quickly test queries and transactions.

Start

make start
This builds the binary, initializes chain data, and starts a single validator node. It handles cleanup automatically — existing chain state is reset on each run. The chain uses:
  • Chain ID: demo
  • Pre-funded accounts: alice, bob
  • Default denomination: stake

Stop

Press Ctrl+C in the terminal running make start.

Reset chain state

make start
Re-running make start resets state automatically. There is no separate reset command.

Localnet (multi-validator)

Use localnet when you want a setup that is closer to a real network. It runs multiple validators in Docker so you can test multi-node behavior locally. For a multi-validator setup using Docker:
# Initialize localnet configuration
make localnet-init

# Start all validators
make localnet-start

# View logs
make localnet-logs

# Stop
make localnet-stop

# Clean all localnet data
make localnet-clean

CLI reference

Once the chain is running, these are the core CLI commands you’ll use to inspect state and submit transactions.

Query commands

Use query commands to read module state without changing anything on-chain.
# Query the current counter value
exampled query counter count

# Query the module parameters
exampled query counter params

# Query with a specific node (if not using default localhost:26657)
exampled query counter count --node tcp://localhost:26657

Transaction commands

Use transaction commands to submit state-changing messages to the chain.
# Add to the counter
exampled tx counter add 10 --from alice --chain-id demo --yes

# Add with a gas limit
exampled tx counter add 10 --from alice --chain-id demo --gas 200000 --yes

# Update module parameters (requires governance authority)
exampled tx counter update-params --from alice --chain-id demo --yes

Useful flags

These flags are the ones you’ll use most often while iterating locally.
FlagDescription
--fromKey name or address to sign with
--chain-idChain ID (use demo for local)
--yesSkip confirmation prompt
--gasGas limit for the transaction
--nodeRPC endpoint (default: tcp://localhost:26657)
--output jsonOutput response as JSON

Node Configuration

When you run make start, the chain creates ~/.exampleapp/config/ automatically and initializes two config files inside it:
FileWhat it controls
app.tomlSDK application settings: gas prices, pruning, API/gRPC servers, telemetry
config.tomlCometBFT settings: peer networking, consensus timeouts, mempool, RPC

app.toml

The most common settings to change during development:
SettingDefaultDescription
minimum-gas-prices"0stake"Minimum fee the node accepts before processing a transaction
pruning"default"How much historical state to keep (default, nothing, everything, custom)
api.enabletrueEnables the REST API on port 1317
grpc.enabletrueEnables the gRPC server on port 9090

config.toml

The settings most likely to change during development:
SettingDefaultDescription
moniker"test"Human-readable name for the node
log_level"info"Log verbosity (debug, info, error)
consensus.timeout_commit"5s"How long to wait after a block is committed before starting the next one
p2p.seeds""Seed nodes to connect to on a live network
p2p.persistent_peers""Peers to maintain permanent connections to

Unit tests

Start here when you want fast feedback on module logic without running a chain. These tests isolate the keeper and gRPC servers from the rest of the app. The unit test logic lives in the counter keeper package on main: the shared suite setup is in x/counter/keeper/keeper_test.go, message-path tests are in x/counter/keeper/msg_server_test.go, and query-path tests are in x/counter/keeper/query_server_test.go. The keeper test suite covers the keeper, msg server, and query server in isolation using an in-memory store and a mock bank keeper. No running chain is required.
go test ./x/counter/...
To run with verbose output:
go test -v ./x/counter/...
To run a specific test:
go test -v -run TestKeeperTestSuite/TestAddCount ./x/counter/...
The test suite is structured around three files:
FileTests
keeper/keeper_test.goGenesis, GetCount, AddCount, SetParams
keeper/msg_server_test.goMsgAdd, event emission, MsgUpdateParams
keeper/query_server_test.goQueryCount, QueryParams

E2E tests

Run E2E tests when you want to verify the full request path against a real node. They give you higher confidence than unit tests, but take longer to complete. The E2E logic lives on main in tests/counter_test.go, which starts an in-process network, builds signed transactions, and verifies query results. The shared network fixture it uses is defined in tests/test_helpers.go. The E2E test suite starts a real in-process validator network and submits actual transactions against it. This tests the full stack: transaction encoding, message routing, keeper logic, and query responses.
go test -v -run TestE2ETestSuite ./tests/...
E2E tests take longer than unit tests because they spin up a real node. Run them before merging significant changes.

Simulation tests

Simulation tests stress the chain with randomized activity to catch edge cases that targeted tests can miss. In this repo, that simulation flow is built with simsx, the Cosmos SDK’s higher-level simulation framework for defining random on-chain activity at the module level. The top-level simulation test commands on main run through sim_test.go. The counter module’s simsx registration lives in x/counter/module.go, the random MsgAdd generation lives in x/counter/simulation/msg_factory.go, and randomized counter genesis lives in x/counter/simulation/genesis.go. In practice, simsx lets each module describe three things: how to generate random starting state, which operations can happen during simulation, and how often each operation should be chosen. For x/counter, that means generating a random initial counter value, registering MsgAdd as a simulation operation, and assigning it a weight so the simulator knows how frequently to try it relative to other module operations. When you run a simulation target, the test harness repeatedly builds app instances, creates random accounts and balances, generates random transactions from the registered module operations, and executes them over many blocks. That makes simsx useful for catching issues that are hard to cover with hand-written tests, like state machine bugs, unexpected panics, invariant violations, and non-deterministic behavior across runs. Simulation runs the chain with randomly generated transactions to detect non-determinism and invariant violations.
# Full simulation
make test-sim-full

# Determinism check
make test-sim-determinism

# All simulation tests
make test-sim
Simulation requires the sims build tag, which the Makefile targets handle automatically.

Lint

Linting is the quickest way to catch style problems and common code-quality issues before CI or code review does. The lint commands are defined in the repo Makefile, which installs golangci-lint and runs it across the full module tree.
make lint
This installs and runs golangci-lint across the repository. To auto-fix issues where possible:
make lint-fix

Test summary

Use this table as a quick reference for choosing the right validation command for the kind of change you made.
CommandWhat it validates
go test ./x/counter/...Keeper, MsgServer, QueryServer in isolation
go test -run TestE2ETestSuite ./tests/...Full transaction and query flow on a live node
make test-sim-fullNon-determinism and invariant violations
make lintCode style and static analysis