Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Testing with Jest and React Native Testing Library

Testing with Jest and React Native Testing Library

~17 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026

Expo projects use Jest by default instead of Vitest from "React for Professionals" chapter 44 (Vitest is Vite-specific, Expo uses Metro as its bundler) – but the API is nearly identical, exactly as React Native Testing Library ("RNTL") is the direct counterpart to React Testing Library, just for native elements instead of the DOM.

Installation

npx expo install jest-expo --dev
npm install --save-dev @testing-library/react-native jest
package.json
{
  "scripts": {
    "test": "jest"
  },
  "jest": {
    "preset": "jest-expo"
  }
}

jest-expo is a preconfigured Jest preset SPECIFICALLY for Expo projects – it automatically handles the right transform for .tsx files, native module mocks (e.g. for expo-image), and other Expo-specific setup details you'd otherwise have to configure by hand.

Testing the Zustand store in isolation

Just like "React for Professionals" chapter 44, we start with the SIMPLEST case – particularly convenient here, since useCartStore itself can be called outside a React component:

store/cartStore.test.ts
import { describe, it, expect, beforeEach } from '@jest/globals';
import { useCartStore } from './cartStore';

describe('useCartStore', () => {
  beforeEach(() => {
    useCartStore.setState({ cart: [] }); // reset the store before every test
  });

  it('starts with an empty cart', () => {
    expect(useCartStore.getState().cart).toEqual([]);
  });

  it('adds a product', () => {
    useCartStore.getState().addProduct({ sku: 'a1', name: 'Test', price: 10 });
    expect(useCartStore.getState().cart).toHaveLength(1);
  });

  it('removes a product by sku', () => {
    useCartStore.getState().addProduct({ sku: 'a1', name: 'Test', price: 10 });
    useCartStore.getState().removeProduct('a1');
    expect(useCartStore.getState().cart).toEqual([]);
  });
});

useCartStore.setState(...)/.getState() – Zustand stores are ALSO fully usable OUTSIDE React components (the useCartStore(selector) hook is just ONE way to access the store) – ideal for tests that don't need to render a component. beforeEach resets the store before EVERY individual test, so tests don't affect each other (without the persist middleware in tests, see the warning below).

Achtung: In real tests, persist (the AsyncStorage middleware from chapter 2) would try to access native AsyncStorage, which doesn't exist in the Jest test environment. jest-expo AUTOMATICALLY mocks AsyncStorage with an in-memory version – tests still run despite this, WITHOUT real persistence between test runs.

Testing ProductCard with RNTL

components/ProductCard.test.tsx
import { describe, it, expect, jest } from '@jest/globals';
import { render, screen, fireEvent } from '@testing-library/react-native';
import ProductCard from './ProductCard';

describe('ProductCard', () => {
  const defaultProps = {
    name: 'Hiking Boots',
    price: 89.99,
    imageUrl: 'https://example.com/boots.jpg',
    onPress: jest.fn(),
    isFavorite: false,
    onToggleFavorite: jest.fn(),
  };

  it('displays the name and price', () => {
    render(<ProductCard {...defaultProps} />);
    expect(screen.getByText('Hiking Boots')).toBeTruthy();
    expect(screen.getByText('$89.99')).toBeTruthy();
  });

  it('calls onPress when the card is pressed', () => {
    render(<ProductCard {...defaultProps} />);
    fireEvent.press(screen.getByText('Hiking Boots'));
    expect(defaultProps.onPress).toHaveBeenCalledTimes(1);
  });

  it('calls onToggleFavorite without triggering onPress', () => {
    render(<ProductCard {...defaultProps} />);
    fireEvent.press(screen.getByText('♡'));
    expect(defaultProps.onToggleFavorite).toHaveBeenCalledTimes(1);
    expect(defaultProps.onPress).not.toHaveBeenCalled();
  });
});
LibraryAPI difference
React Testing Library (web)fireEvent.click(...), searches/compares against REAL DOM nodes (toBeInTheDocument()).
React Native Testing LibraryfireEvent.press(...) (there's no "click" on mobile devices), works with React Native's OWN element tree, not the DOM (toBeTruthy() instead of toBeInTheDocument()).

The third test example INDIRECTLY verifies the same thing as "React for Professionals" chapter 44: that event.stopPropagation() in handleToggleFavorite actually works – a bug in that line would additionally trigger onPress and make this test fail.

Bonus: custom mocks for native modules

Some libraries need an EXPLICIT mock that jest-expo doesn't provide automatically – e.g. react-native-reanimated:

// jest.setup.js:
import 'react-native-reanimated/jestSetup';

Tipp: Rule of thumb identical to "React for Professionals" chapter 44: test VISIBLE behavior (what text appears, which callbacks get called), not internal implementation. A test that checks screen.getByText(...) instead of an internal state variable stays valid even after a refactor – exactly what we already prepared by making isFavorite a PROP instead of local state back in chapter 3.