Week 4 • Client Testing Strategy Lite Unit And Integration
Client Testing Strategy: Lite Unit & Integration
A practical testing strategy for Solana client code: fast unit checks plus one meaningful integration path.
Step 1: 0-to-1 Theory
Primitives
unit test: tests pure functions without networkintegration test: tests feature path across service + statefailure-path test: proves behavior under expected errors
Mental Model
Solana client bugs usually show up as:
- wrong inputs (amounts, addresses, cluster)
- wrong state (wallet disconnected, stale blockhash)
- wrong expectations (sent vs confirmed)
Tests should match that reality. Start with pure functions (fast) and add one integration path (realistic).
Invariants
- Happy-path-only tests are not enough.
- Error mapping logic must be unit tested.
- One stable integration path must run in CI.
Quick Checks
- Why are failure-path tests mandatory in wallet apps?
- What should be mocked vs real in integration tests?
Step 2: Real-World Implementation (Code Solution)
Install test stack if needed:
pnpm add -D vitest @testing-library/react @testing-library/jest-dom jsdomCreate lib/solana/__tests__/uiTxState.test.ts:
import { describe, it, expect } from "vitest";
import { classifyTxError } from "../uiTxState";
describe("classifyTxError", () => {
it("maps rejection", () => {
// User rejection should be treated as a normal user action, not a crash.
expect(classifyTxError("User rejected request")).toBe("user-rejected");
});
it("maps blockhash expiry", () => {
// Expired blockhash usually means rebuild + re-sign is required.
expect(classifyTxError("Blockhash not found")).toBe("expired");
});
it("maps unknown", () => {
// Unknown errors are expected; we still want deterministic UX behavior.
expect(classifyTxError("strange failure")).toBe("unknown");
});
});Create components/solana/__tests__/TxActionCard.test.tsx:
import { describe, it, expect } from "vitest";
import { render, screen } from "@testing-library/react";
import { TxActionCard } from "../TxActionCard";
describe("TxActionCard", () => {
it("renders default status", () => {
// Smoke test: component renders and shows a status label.
render(<TxActionCard />);
expect(screen.getByText(/Status:/)).toBeTruthy();
});
});Add script:
{
"scripts": {
"test": "vitest run"
}
}Run:
pnpm testExpected result:
- unit tests verify error classification behavior.
Step 3: Mastery Test
- Easy: what is the fastest pure function to test first?
- Medium: where should integration tests stop (UI only vs RPC)?
- Hard: how would you design deterministic tests for retry logic?