App Integration Tests
Overview
App integration level tests leverage a custom construction of the pocket appchain (for testing only).
This construction integrates all the pocket modules (and their cosmos-sdk dependencies) and exercises the appchain's message routing/handling and transaction processing logic.
Tests in this level conventionally use the testutil/integration package's App structure and constructors to set up the appchain, execute messages, and make assertions against the resulting appchain state.
Using integration.App
integration.AppConstructors
To create a new instance of the IntegrationApp for your tests, use the NewCompleteIntegrationApp constructor, which handles the setup of all modules, multistore, base application, etc:
// NewCompleteIntegrationApp creates a new instance of the App, abstracting out
// all the internal details and complexities of the application setup.
func NewCompleteIntegrationApp(t *testing.T, opts ...IntegrationAppOptionFn) *App
// IntegrationAppOptionFn is a function that receives and has the opportunity to
// modify the IntegrationAppConfig. It is intended to be passed during integration
// App construction to modify the behavior of the integration App.
type IntegrationAppOptionFn func(*IntegrationAppConfig)If more granular control over the application configuration is required, the more verbose NewIntegrationApp constructor exposes additional parameters:
// NewIntegrationApp creates a new instance of the App with the provided details
// on how the modules should be configured.
func NewIntegrationApp(
t *testing.T,
sdkCtx sdk.Context,
cdc codec.Codec,
txCfg client.TxConfig,
registry codectypes.InterfaceRegistry,
bApp *baseapp.BaseApp,
logger log.Logger,
authority sdk.AccAddress,
modules map[string]appmodule.AppModule,
keys map[string]*storetypes.KVStoreKey,
msgRouter *baseapp.MsgServiceRouter,
queryHelper *baseapp.QueryServiceTestHelper,
opts ...IntegrationAppOptionFn,
) *App {
// ...
}Customizing integration.App Configuration
If the existing IntegrationAppConfig is insufficient, it may be extended with additional fields, corresponding logic, and IntegrationAppOptionFn s to set them.
Module Configuration
Integrated modules can be configured using IntegrationAppOptionFn typed option functions.
Example use cases include:
Setting custom genesis states for one or more modules (see
integration.WithModuleGenesisState()).Setting up a faucet account (see:
newFaucetInitChainerFn()).Collecting module info (see:
newInitChainerCollectModuleNames()).
Setting Module Genesis State
supplierGenesisState := &suppliertypes.GenesisState{
// ...
}
app := NewCompleteIntegrationApp(t,
WithModuleGenesisState[suppliermodule.AppModule](supplierGenesisState),
)Message / Transaction / Block Processing
The IntegrationApp provides several methods to manage the lifecycle of transactions and blocks during tests:
RunMsg/RunMsgs: Processes one or more messages by:calling their respective handlers
packaging them into a transaction
finalizing the block
committing the state
advancing the block height
returning the message responses
NextBlock/NextBlocks: Only advances the blockchain state to subsequent blocks.
Example Test
Here's a simple example of how to create a new integration app instance and run a message using the helper functions:
func TestAppIntegrationExample(t *testing.T) {
// Initialize a new complete integration app with default options.
app := NewCompleteIntegrationApp(t)
// Example message to be processed
msg := banktypes.NewMsgSend(fromAddr, toAddr, sdk.NewCoins(sdk.NewInt64Coin("upokt", 100)))
// Run the message in the integration app
res, err := app.RunMsg(t, msg)
require.NoError(t, err)
// Check the result
require.NotNil(t, res, "Expected a valid response for the message")
// Type assert the result to the message response type
sendRes, ok := res.(*banktypes.MsgSendResponse)
require.True(t, ok)
require.NotNil(t, sendRes)
}This example initializes the app, processes a bank message, and validates the result.
Was this helpful?
