Skip to content

Commit

Permalink
[Testing Docs] Add initial testing docs (#871)
Browse files Browse the repository at this point in the history
## Summary

Adds the following pages to a new "Develop" > "Testing" section:

- Testing Levels
- Module Integration Tests
- App Integration Tests
- App Integration Suites
- In-Memory Network Integration Tests
- End-to-End Tests

## Issue

N/A

## Type of change

Select one or more from the following:

- [ ] New feature, functionality or library
- [ ] Consensus breaking; add the `consensus-breaking` label if so. See
#791 for details
- [ ] Bug fix
- [ ] Code health or cleanup
- [x] Documentation
- [ ] Other (specify)

## Testing

- [x] **Documentation**: `make docusaurus_start`; only needed if you
make doc changes
- [ ] **Unit Tests**: `make go_develop_and_test`
- [ ] **LocalNet E2E Tests**: `make test_e2e`
- [ ] **DevNet E2E Tests**: Add the `devnet-test-e2e` label to the PR.

## Sanity Checklist

- [ ] I have tested my changes using the available tooling
- [ ] I have commented my code
- [x] I have performed a self-review of my own code; both comments &
source code
- [ ] I create and reference any new tickets, if applicable
- [x] I have left TODOs throughout the codebase, if applicable

---------

Co-authored-by: Daniel Olshansky <[email protected]>
  • Loading branch information
bryanchriswhite and Olshansk authored Oct 24, 2024
1 parent 0663679 commit d038470
Show file tree
Hide file tree
Showing 8 changed files with 427 additions and 14 deletions.
6 changes: 0 additions & 6 deletions docusaurus/docs/develop/developer_guide/test_suites.md

This file was deleted.

135 changes: 135 additions & 0 deletions docusaurus/docs/develop/developer_guide/testing/app_integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
---
sidebar_position: 3
title: App Integration Tests
---

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

## Table Of Contents <!-- omit in toc -->

- [Overview](#overview)
- [Using `integration.App`](#using-integrationapp)
- [Constructors](#constructors)
- [Customizing `integration.App` Configuration](#customizing-integrationapp-configuration)
- [Module Configuration](#module-configuration)
- [Setting Module Genesis State](#setting-module-genesis-state)
- [Message / Transaction / Block Processing](#message--transaction--block-processing)
- [Example Test](#example-test)

## Overview

[**App integration level**](testing_levels#app-integration-tests) 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](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.:

```go
// 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:

```go
// 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`](https://github.com/pokt-network/poktroll/blob/main/testutil/integration/options.go#L13) 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 one or more modules (see [`integration.WithModuleGenesisState()`](https://github.com/pokt-network/poktroll/blob/main/testutil/integration/options.go#L40)).
- Setting up a faucet account (see: [`newFaucetInitChainerFn()`](https://github.com/pokt-network/poktroll/blob/main/testutil/integration/app.go#L985)).
- Collecting module info (see: [`newInitChainerCollectModuleNames()`](https://github.com/pokt-network/poktroll/blob/main/testutil/integration/suites/base.go#L157)).
#### Setting Module Genesis State
```go
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:
```go
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.
6 changes: 6 additions & 0 deletions docusaurus/docs/develop/developer_guide/testing/e2e.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
sidebar_position: 7
title: End-to-End Tests
---

// TODO_DOCUMENT(@bryanchriswhite)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
sidebar_position: 6
title: In-Memory Network Integration Tests
---

// TODO_DOCUMENT(@bryanchriswhite): Explain cosmos-sdk in-memory network and its appropriate usage.
114 changes: 114 additions & 0 deletions docusaurus/docs/develop/developer_guide/testing/integration_suites.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
sidebar_position: 5
title: App Integration Suites
---

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

## Table of Contents <!-- omit in toc -->

- [Overview](#overview)
- [When to Use Test Suites](#when-to-use-test-suites)
- [Using an Existing Integration Suite](#using-an-existing-integration-suite)
- [Example (`ParamsSuite`)](#example-paramssuite)
- [Implementing a Test Suite](#implementing-a-test-suite)
- [Test Suite Gotchas](#test-suite-gotchas)

## Overview

The [`suites` package](https://github.com/pokt-network/poktroll/tree/main/testutil/integration/suites) provides interfaces and base implementations for creating and managing **app integration test** suites.

The foundational components are:

- [**`IntegrationSuite`**](https://github.com/pokt-network/poktroll/blob/main/testutil/integration/suites/interface.go#L14): An interface defining common methods for interacting with an integration app.
- [**`BaseIntegrationSuite`**](https://github.com/pokt-network/poktroll/blob/main/testutil/integration/suites/base.go#L26): A base implementation of the `IntegrationSuite` interface that can be extended by embedding in other test suites.

## When to Use Test Suites

- **Complex Integration Tests**: Testing interactions between several modules; suites facilitate encapsulation and decomposition.
- **Complex Scenarios**: Simulating real-world scenarios that involve several transactions, state changes, and/or complex assertion logic.
- **Reusable Components**: To DRY (Don't Repeat Yourself) up common test helpers which can be embedded in other test suites (object oriented).

## Using an Existing Integration Suite

The `testutil/integration/suites` package contains multiple **app integration suites** which are intended to be embedded in [**app integration level**](testing_levels#app-integration-tests) test suites.

### Example (`ParamsSuite`)

The following example shows a test suite which embeds `suites.ParamsSuite`, in order to set on-chain module params as part of its `SetupTest()` method:

```go
package suites

import (
"testing"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
cosmostypes "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
)

type ExampleTestSuite struct {
suites.ParamsSuite
}

// SetupTest is called before each test method in the suite.
func (s *ExampleTestSuite) SetupTest() {
// Initialize a new app instance for each test.
s.app = NewApp(s.T())

// Setup the authz accounts and grants for updating parameters.
s.SetupTestAuthzAccounts()
s.SetupTestAuthzGrants()

// Set the module params using the ParamsSuite.
s.RunUpdateParam(s.T(),
sharedtypes.ModuleName,
string(sharedtypes.KeyNumBlocksPerSession),
9001,
)
}

func (s *ExampleTestSuite) TestExample() {
// Query module params using the ParamsSuite.
sharedParams, err := s.QueryModuleParams(s.T(), sharedtypes.ModuleName)
require.NoError(s.T(), err)

// Utilize other BaseIntegrationSuite methods to interact with the app...

fundAmount := int64(1000)
fundAddr, err := cosmostypes.AccAddressFromBech32("cosmos1exampleaddress...")
require.NoError(s.T(), err)

// Fund an address using the suite's FundAddress method.
s.FundAddress(s.T(), fundAddr, fundAmount)

// Use the bank query client to verify the balance.
bankQueryClient := s.GetBankQueryClient()
balRes, err := bankQueryClient.Balance(s.SdkCtx(), &banktypes.QueryBalanceRequest{
Address: fundAddr.String(),
Denom: "upokt",
})

// Validate the balance.
require.NoError(s.T(), err)
require.Equal(s.T(), fundAmount, balRes.GetBalance().Amount.Int64())
}

// Run the ExampleIntegrationSuite.
func TestExampleTestSuite(t *testing.T) {
suite.Run(t, new(ExampleTestSuite))
}
```

## Implementing a Test Suite

// TODO_DOCUMENT(@bryanchriswhite)

### Test Suite Gotchas

- **Setup**: You MAY need to call `SetupXXX()`: check embedded suites for any required setup and copy-paste
- **Accessing Test State**: Avoid using `s.T()` in methods of suites which are intended to be embedded in other suites; pass a `*testing.T` argument instead.
- **Inheritance**: Inheriting multiple suites is hard since only one can be embedded anonymously: others will have to accessed via a named field.

// TODO_DOCUMENT(@bryanchriswhite): Add a `testutil/integration/suites.doc.go` with testable examples.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
sidebar_position: 2
title: Module Integration Tests
---

// TODO_DOCUMENT(@bryanchriswhite): Explain `testkeeper.NewTokenomicsKeepers()` (to be renamed) and its appropriate usage.
Loading

0 comments on commit d038470

Please sign in to comment.