Skip to main content

Integration Testing

The dApp template includes an IntegrationTests/ project for end-to-end testing against the full Aspire stack. Unlike the fast unit tests that use an embedded DevChain, integration tests connect to a running AppHost over HTTP — testing the real network stack, service discovery, and indexer processing.

Prerequisites

The AppHost must be running:

dotnet run --project AppHost

Wait until all services show healthy in the Aspire dashboard before running tests.

Run the Tests

dotnet test IntegrationTests/

By default, the tests connect to http://localhost:8545. If the DevChain is on a different port (Aspire assigns dynamic ports), set the DEVCHAIN_URL environment variable:

DEVCHAIN_URL=http://localhost:12345 dotnet test IntegrationTests/

What the Template Tests

The template ships with three E2E tests:

1. DevChain Health Check

Verifies the DevChain is running and responding to JSON-RPC:

var web3 = new Web3(rpcUrl);
var blockNumber = await web3.Eth.Blocks.GetBlockNumber.SendRequestAsync();
Assert.True(blockNumber.Value >= 0);

2. Deploy and Query

Deploys MyToken to the live DevChain and verifies the contract state:

var deployment = new MyTokenDeployment
{
Name = "E2EToken",
Symbol = "E2E",
InitialSupply = Web3.Convert.ToWei(1_000_000)
};

var receipt = await MyTokenService.DeployContractAndWaitForReceiptAsync(web3, deployment);
var tokenService = new MyTokenService(web3, receipt.ContractAddress);

var name = await tokenService.NameQueryAsync();
Assert.Equal("E2EToken", name);

3. Transfer with Indexing

Deploys a token, transfers tokens, then waits for the Indexer to process the transaction. This tests the full pipeline — DevChain → Indexer → PostgreSQL:

var transferReceipt = await tokenService.TransferRequestAndWaitForReceiptAsync(
DevChainAccounts.Account3.Address,
Web3.Convert.ToWei(100));

// Wait for indexer to process
await Task.Delay(3000);

// Query the chain to verify
var balance = await tokenService.BalanceOfQueryAsync(DevChainAccounts.Account3.Address);
Assert.Equal(Web3.Convert.ToWei(100), balance);

The Task.Delay(3000) gives the Indexer time to pick up and process the new block. In production tests, you'd poll a health endpoint or database query instead.

Writing Your Own Integration Tests

Integration tests are best for scenarios that span multiple services:

  • Contract deploys visible in Explorer — deploy, wait for indexing, query the Explorer's API
  • Token transfers indexed correctly — transfer tokens, verify the Indexer wrote them to PostgreSQL
  • Load generator creates expected transactions — verify transaction count increases over time
  • ABI auto-discovery works — deploy a contract from the Foundry project, verify the Explorer resolves the ABI

When to Use Integration Tests vs Unit Tests

AspectC# Unit Tests (Tests/)Integration Tests (IntegrationTests/)
RequiresNothing (in-process)Running AppHost + Docker
SpeedSecondsMinutes (startup + processing)
TestsContract logic via typed servicesFull pipeline across services
NetworkIn-process, no HTTPReal HTTP, real ports
Use forTDD loop, fast feedbackCI pipeline, deployment verification

Use unit tests during development (fast feedback), integration tests before merging or deploying (full confidence).

Next Steps

With both testing tiers covered, move on to building the user-facing dApp: