# Testing Strategy

This page explains the principles behind our testing approach, why we favor integration tests for UI validation, and the reasoning behind the custom test renderer.

---

## Testing Principles

Our testing strategy is built on three core principles:

### 1. Focus on your components

Never test external library components. It's not your responsibility to validate third-party behavior. Your tests should verify that _your_ code works correctly, not that React, Redux, or any library does what its own tests already verify.

### 2. Favor integration tests for UI

When testing UI features with many components, integration tests provide better coverage than unit tests. An integration test exercises the full feature flow — from user interaction to state changes to UI updates — which catches issues that unit tests miss.

This applies specifically to UI testing. Pure helper functions, Redux reducers, and isolated logic still benefit from focused unit tests. The integration-first approach is for feature-level UI validation, where rendering the full feature with real providers catches more real-world bugs than testing components in isolation.

### 3. Tests should be maintainable

Avoid testing implementation details that may change. If a test breaks every time you refactor internals without changing behavior, the test is too tightly coupled. Use queries based on user-visible behavior (`ByRole`, `ByText`) rather than internal structure (`ByTestId` as a last resort).

---

## Why a Custom Test Renderer

The Ledger Wallet apps depend on many providers: Redux, theming, i18n, feature flags, navigation, analytics, React Query, and more. Before the custom renderer existed, every test had to manually wrap components in a stack of providers:

```tsx
// Before: manual provider stacking
<Provider store={mockStore}>
  <FirebaseFeatureFlagsProvider getFeature={getFeature}>
    <NavigationContainer>
      <ThemeProvider theme={theme}>
        <I18nextProvider i18n={i18n}>
          <MyComponent />
        </I18nextProvider>
      </ThemeProvider>
    </NavigationContainer>
  </FirebaseFeatureFlagsProvider>
</Provider>
```

This created several problems:

- **Boilerplate in every test file** — The provider stack was duplicated across hundreds of test files.
- **Fragile mocks** — Tests mocked `useSelector`, `useTheme`, `useTranslation`, and other hooks individually. When provider implementations changed, many mocks broke simultaneously.
- **Incomplete test environments** — Tests often ran with only a subset of providers, hiding bugs that only appeared when all providers were present.

The custom renderer solves these problems by providing a single `render()` call that includes all providers with sensible defaults. Tests use the real Redux store, real theming, real i18n — and only override what they need via `overrideInitialState` or `initialState`.

### Key Benefits

| Benefit                        | Description                                                                   |
| ------------------------------ | ----------------------------------------------------------------------------- |
| **No provider mocking needed** | Redux, themes, analytics, i18n, feature flags, routing are all pre-configured |
| **No selector mocking**        | Use `overrideInitialState` or `initialState` to set up state                  |
| **Feature flags ready**        | Access feature flags via `FirebaseFeatureFlagsProvider`                       |
| **Routing included**           | `NavigationContainer` (Mobile) or `MemoryRouter` (Desktop)                    |
| **User events included**       | Access `user` from render result for realistic interactions                   |
| **Store access**               | Access `store` from render result to dispatch or read state                   |
| **Full RTL API**               | All `@testing-library` utilities are re-exported                              |

---

## Further Reading

These external resources provide additional context on the testing philosophy we follow:

- [Common mistakes with React Testing Library](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library)
- [Write fewer, longer tests](https://kentcdodds.com/blog/write-fewer-longer-tests)
- [Static vs Unit vs Integration vs E2E Testing](https://kentcdodds.com/blog/static-vs-unit-vs-integration-vs-e2e-tests)
- [Testing Overview - React](https://reactjs.org/docs/testing.html)
- [Stop mocking fetch](https://kentcdodds.com/blog/stop-mocking-fetch)

---

## See also

- [Testing reference](../reference/testing) — Tools, coverage targets, renderer API, query priority
- [How to Write Tests](../how-to/write-tests) — Step-by-step guide for writing tests
- [How to Use the Custom Renderer](../how-to/use-custom-renderer) — Practical usage guide
