Skip to main content

App Integration Tests

// TODO(@bryanchriswhite): Replace github source links with godocs links once available.

Table Of Contents

Overview

App integration level tests leverage a custom construction of the poktroll appchain (for testing only).

This construction integrates all the poktroll 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.

info

See App Integration Suites for organizing larger or higher-level app integration tests.

Using integration.App

Constructors

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 IntegrationAppOptionFns to set them.

Module Configuration

Integrated modules can be configured using IntegrationAppOptionFn typed option functions.

Example use cases include:

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.