Introduktion: Hvorfor debugging og test betyder alt i 2026
Okay, lad os bare sige det som det er — React Native-økosystemet har ændret sig vildt meget de seneste par år. Den nye arkitektur med JSI, Fabric og TurboModules er nu standard, Expo SDK 55 kører med React Native 0.83, og Hermes v1 er den primære JavaScript-motor. Mulighederne for at bygge hurtige mobilapps har simpelthen aldrig været bedre.
Men med al den ekstra kraft følger der også mere kompleksitet. Og det betyder, at du har brug for solide debugging- og teststrategier.
I denne guide dykker vi ned i de vigtigste værktøjer og metoder til debugging og test af React Native-apps i 2026. Vi kommer omkring alt fra React Native DevTools og Radon IDE til testframeworks som Jest, React Native Testing Library, Detox og Maestro. Uanset om du lige er startet med React Native, eller du har bygget apps i årevis, så er målet her at give dig praktiske færdigheder, du kan bruge med det samme.
Del 1: Debugging-værktøjer i React Native
React Native DevTools — den nye standard
Med React Native 0.76 fik vi React Native DevTools som det officielle debugging-værktøj. Det erstattede de tidligere løsninger som Flipper, Experimental Debugger og Hermes debugger (ærligt talt savner de færreste Flipper). Værktøjet er bygget på den samme frontend som Chrome DevTools, så hvis du har erfaring med webudvikling, vil du føle dig hjemme med det samme.
Du starter React Native DevTools på én af to måder:
- Åbn Dev Menu i din app og vælg "Open DevTools"
- Tryk på "j" i terminalen, hvor Metro bundler kører
Console-panelet
Console-panelet er der, hvor du inspicerer logs fra din app. Alle console.log(), console.warn() og console.error() kald vises med fuld stack trace-information. Du kan også køre JavaScript-udtryk direkte i konsollen — det er fantastisk til at inspicere variabler og objekter i realtid.
// Eksempel på struktureret logging
console.log('Brugerdata hentet:', JSON.stringify(userData, null, 2));
console.warn('API-kald tog mere end 2 sekunder:', responseTime);
console.error('Fejl ved login:', error.message, error.stack);
Sources-panelet og breakpoints
Sources-panelet lader dig gennemse kildefiler og sætte breakpoints, altså steder hvor din app skal pause udførelsen. Når appen pauser, kan du inspicere variabler, call stack og scope. Der er flere typer breakpoints, og det er værd at kende dem alle:
- Linje-breakpoints: Klik på linjenummeret for at pause ved en bestemt linje
- Betingede breakpoints: Højreklik og vælg "Add conditional breakpoint" for kun at pause, når en bestemt betingelse er opfyldt
- Logpoints: Logger en meddelelse uden at pause udførelsen — virkelig nyttigt til produktion-lignende debugging
// Eksempel: Sæt et betinget breakpoint på denne linje
// Betingelse: items.length > 100
const processItems = (items) => {
const filtered = items.filter(item => item.isActive); // Breakpoint her
return filtered.map(item => ({
...item,
processedAt: new Date().toISOString(),
}));
};
Network-panelet
Network-panelet viser alle netværksanmodninger fra din app. Hver anmodning viser timing, headers og forhåndsvisning af svaret. Lige nu understøtter panelet fetch(), XMLHttpRequest og <Image> — understøttelse af tilpassede netværksbiblioteker er på vej, men endnu ikke klar.
React Components og Profiler
De integrerede Components- og Profiler-paneler giver dig alle funktioner fra React DevTools browserudvidelsen. Med Components-panelet kan du inspicere komponenttræet, se props og state for hver komponent, og endda ændre værdier i realtid. Profiler-panelet måler renderingstider og hjælper med at finde performance-flaskehalse — noget vi vender tilbage til senere i guiden.
Radon IDE — alt-i-en debugging direkte i din editor
Radon IDE fra Software Mansion er efter min mening et af de mest spændende debugging-værktøjer lige nu. Det er en VSCode- og Cursor-udvidelse, der gør din editor til et fuldt udstyret React Native IDE. Det bedste? Du behøver ikke skifte mellem forskellige apps hele tiden.
Nøglefunktioner
- Zero-config debugger: Breakpoints fungerer i VSCode uden ekstra opsætning for React Native- og Expo-projekter
- Element Inspector: Peg på et element i forhåndsvisningen for at springe direkte til komponentens kildekode
- Netværkspanel: Inspicér din apps netværksaktivitet direkte i editoren
- Tredjeparts DevTools: Integreret support for React Query DevTools, Redux DevTools og mere
- Radon Connect: Tilslut debuggeren til fysiske enheder og eksterne simulatorer
- Betingede breakpoints og exception-filtre: Avanceret debugging med betingelses- og udtryks-breakpoints samt uncaught/caught exception-filtre
// Med Radon IDE kan du inspicere direkte i VSCode
// Konsollinks linker automatisk tilbage til din kildekode
// Sæt breakpoint her og inspicér "response" i Variables-panelet
const fetchUserProfile = async (userId: string) => {
try {
const response = await api.get(`/users/${userId}`);
return response.data;
} catch (error) {
console.error('Fejl ved hentning af brugerprofil:', error);
throw error;
}
};
Reactotron — din ven til state-debugging
Reactotron er en gratis open source-desktopapp fra Infinite Red, der er bygget specifikt til inspektion af React og React Native-apps. Hvis du arbejder meget med Redux eller anden state management, er Reactotron guld værd.
Opsætning af Reactotron
# Installation
npm install --save-dev reactotron-react-native
# Hvis du bruger Redux
npm install --save-dev reactotron-redux
// ReactotronConfig.js
import Reactotron from 'reactotron-react-native';
import { reactotronRedux } from 'reactotron-redux';
const reactotron = Reactotron
.configure({ name: 'MinApp' })
.useReactNative({
asyncStorage: true,
networking: { ignoreUrls: /symbolicate/ },
editor: true,
errors: { veto: (stackFrame) => false },
overlay: true,
})
.use(reactotronRedux())
.connect();
export default reactotron;
Med Reactotron kan du blandt andet:
- Overvåge Redux actions og state-ændringer i realtid
- Inspicere API-kald og responstider
- Se AsyncStorage-indhold
- Køre custom commands til hurtig debugging
- Benchmarke funktioner og måle performance
Expo DevTools Plugins
Expo har sit eget plugin-system til debugging, og det fungerer i både Development builds og Expo Go. Det smarte er, at disse plugins ikke kræver native moduler eller config plugins — du installerer dem bare og er klar.
# Installation af TanStack Query DevTools plugin
npx expo install @dev-plugins/react-query
# Installation af React Navigation DevTools plugin
npx expo install @dev-plugins/react-navigation
// App.tsx — aktivér plugins i din app
import { useReactQueryDevTools } from '@dev-plugins/react-query';
import { useReactNavigationDevTools } from '@dev-plugins/react-navigation';
import { useNavigationContainerRef } from '@react-navigation/native';
export default function App() {
const navigationRef = useNavigationContainerRef();
useReactQueryDevTools(queryClient);
useReactNavigationDevTools(navigationRef);
return (
<QueryClientProvider client={queryClient}>
<NavigationContainer ref={navigationRef}>
{/* Din app */}
</NavigationContainer>
</QueryClientProvider>
);
}
Del 2: Teststrategier for React Native
Testpyramiden for mobilapps
En god teststrategi for React Native følger den klassiske testpyramide, bare tilpasset til mobiludvikling:
- Unit tests (bunden): Hurtige, isolerede tests af forretningslogik, utility-funktioner og hooks. Det her er rygraden — de udgør størstedelen af dine tests.
- Komponenttests (midten): Tests af React Native-komponenter med brugerinteraktioner, der verificerer at UI'en opfører sig som forventet.
- End-to-end tests (toppen): Tests af hele brugerflows på rigtige enheder eller simulatorer, der tjekker at appen fungerer som helhed.
Denne lagdelte tilgang giver hurtig feedback fra unit tests, mens komponent- og E2E-tests fanger de integrationsfejl og UX-problemer, som unit tests ikke kan opdage.
Jest — fundamentet for al React Native-test
Jest er testframeworket for React Native, og det vedligeholdes af Meta. Det er hurtigt, har god mocking-support og integrerer problemfrit med React Native Testing Library. Så langt, så godt.
Opsætning
Jest er allerede inkluderet i nye React Native- og Expo-projekter. Tjek din package.json:
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
},
"jest": {
"preset": "react-native",
"setupFilesAfterSetup": ["./jest.setup.js"],
"transformIgnorePatterns": [
"node_modules/(?!(react-native|@react-native|@react-navigation|expo)/)"
]
}
}
Unit tests med Jest
// utils/formatCurrency.ts
export const formatCurrency = (
amount: number,
currency: string = 'DKK'
): string => {
return new Intl.NumberFormat('da-DK', {
style: 'currency',
currency,
}).format(amount);
};
// utils/__tests__/formatCurrency.test.ts
import { formatCurrency } from '../formatCurrency';
describe('formatCurrency', () => {
it('formaterer beløb korrekt i DKK', () => {
expect(formatCurrency(1234.56)).toBe('1.234,56 kr.');
});
it('håndterer nul korrekt', () => {
expect(formatCurrency(0)).toBe('0,00 kr.');
});
it('understøtter andre valutaer', () => {
const result = formatCurrency(99.99, 'EUR');
expect(result).toContain('99,99');
});
it('håndterer negative beløb', () => {
const result = formatCurrency(-500);
expect(result).toContain('500');
});
});
Test af custom hooks
// hooks/useCounter.ts
import { useState, useCallback } from 'react';
export const useCounter = (initialValue: number = 0) => {
const [count, setCount] = useState(initialValue);
const increment = useCallback(() => setCount(c => c + 1), []);
const decrement = useCallback(() => setCount(c => c - 1), []);
const reset = useCallback(() => setCount(initialValue), [initialValue]);
return { count, increment, decrement, reset };
};
// hooks/__tests__/useCounter.test.ts
import { renderHook, act } from '@testing-library/react-native';
import { useCounter } from '../useCounter';
describe('useCounter', () => {
it('starter med standardværdi 0', () => {
const { result } = renderHook(() => useCounter());
expect(result.current.count).toBe(0);
});
it('starter med brugerdefineret værdi', () => {
const { result } = renderHook(() => useCounter(10));
expect(result.current.count).toBe(10);
});
it('inkrementerer korrekt', () => {
const { result } = renderHook(() => useCounter());
act(() => result.current.increment());
expect(result.current.count).toBe(1);
});
it('nulstiller til startværdi', () => {
const { result } = renderHook(() => useCounter(5));
act(() => result.current.increment());
act(() => result.current.increment());
act(() => result.current.reset());
expect(result.current.count).toBe(5);
});
});
React Native Testing Library — test som dine brugere oplever det
React Native Testing Library (RNTL) opfordrer dig til at teste komponenter, som brugerne faktisk interagerer med dem — via tekst, roller og tilgængeligheds-labels. Det giver langt mere robuste tests, der ikke bryder bare fordi du refaktorerer den interne implementation. Det er en game-changer, ærligt talt.
Installation
npm install --save-dev @testing-library/react-native
fireEvent vs. userEvent
RNTL giver dig to API'er til at simulere brugerinteraktioner:
- fireEvent: En simpel API, der direkte kalder event handlers. Fin til grundlæggende tests.
- userEvent: En mere realistisk API, der simulerer hele interaktionssekvensen som den sker i React Native runtime. Brug denne til de fleste tests — den fanger flere fejl.
// components/LoginForm.tsx
import React, { useState } from 'react';
import { View, TextInput, Text, Pressable, ActivityIndicator } from 'react-native';
interface LoginFormProps {
onSubmit: (email: string, password: string) => Promise<void>;
}
export const LoginForm: React.FC<LoginFormProps> = ({ onSubmit }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const handleSubmit = async () => {
if (!email || !password) {
setError('Udfyld venligst alle felter');
return;
}
setLoading(true);
setError('');
try {
await onSubmit(email, password);
} catch (e) {
setError('Login mislykkedes. Prøv igen.');
} finally {
setLoading(false);
}
};
return (
<View>
<TextInput
placeholder="E-mail"
value={email}
onChangeText={setEmail}
accessibilityLabel="E-mail input"
keyboardType="email-address"
autoCapitalize="none"
/>
<TextInput
placeholder="Adgangskode"
value={password}
onChangeText={setPassword}
accessibilityLabel="Adgangskode input"
secureTextEntry
/>
{error ? <Text accessibilityRole="alert">{error}</Text> : null}
<Pressable
onPress={handleSubmit}
disabled={loading}
accessibilityRole="button"
accessibilityLabel="Log ind"
>
{loading ? (
<ActivityIndicator />
) : (
<Text>Log ind</Text>
)}
</Pressable>
</View>
);
};
// components/__tests__/LoginForm.test.tsx
import React from 'react';
import { render, screen, waitFor } from '@testing-library/react-native';
import { userEvent } from '@testing-library/react-native';
import { LoginForm } from '../LoginForm';
describe('LoginForm', () => {
const mockOnSubmit = jest.fn();
const user = userEvent.setup();
beforeEach(() => {
mockOnSubmit.mockClear();
});
it('viser fejlmeddelelse ved tomme felter', async () => {
render(<LoginForm onSubmit={mockOnSubmit} />);
const loginButton = screen.getByRole('button', { name: 'Log ind' });
await user.press(loginButton);
expect(screen.getByText('Udfyld venligst alle felter')).toBeTruthy();
expect(mockOnSubmit).not.toHaveBeenCalled();
});
it('kalder onSubmit med korrekte værdier', async () => {
mockOnSubmit.mockResolvedValueOnce(undefined);
render(<LoginForm onSubmit={mockOnSubmit} />);
const emailInput = screen.getByLabelText('E-mail input');
const passwordInput = screen.getByLabelText('Adgangskode input');
const loginButton = screen.getByRole('button', { name: 'Log ind' });
await user.type(emailInput, '[email protected]');
await user.type(passwordInput, 'hemmelig123');
await user.press(loginButton);
await waitFor(() => {
expect(mockOnSubmit).toHaveBeenCalledWith(
'[email protected]',
'hemmelig123'
);
});
});
it('viser fejlmeddelelse ved mislykket login', async () => {
mockOnSubmit.mockRejectedValueOnce(new Error('Ugyldige legitimationsoplysninger'));
render(<LoginForm onSubmit={mockOnSubmit} />);
const emailInput = screen.getByLabelText('E-mail input');
const passwordInput = screen.getByLabelText('Adgangskode input');
const loginButton = screen.getByRole('button', { name: 'Log ind' });
await user.type(emailInput, '[email protected]');
await user.type(passwordInput, 'forkert');
await user.press(loginButton);
await waitFor(() => {
expect(screen.getByText('Login mislykkedes. Prøv igen.')).toBeTruthy();
});
});
it('deaktiverer knappen under indlæsning', async () => {
mockOnSubmit.mockImplementation(
() => new Promise(resolve => setTimeout(resolve, 1000))
);
render(<LoginForm onSubmit={mockOnSubmit} />);
const emailInput = screen.getByLabelText('E-mail input');
const passwordInput = screen.getByLabelText('Adgangskode input');
await user.type(emailInput, '[email protected]');
await user.type(passwordInput, 'hemmelig123');
const loginButton = screen.getByRole('button', { name: 'Log ind' });
await user.press(loginButton);
// Under indlæsning bør knappen være deaktiveret
await waitFor(() => {
expect(screen.getByRole('button', { name: 'Log ind' })).toBeDisabled();
});
});
});
Mocking i React Native-tests
Effektiv mocking er afgørende i React Native-tests, fordi mange native moduler simpelthen ikke er tilgængelige i testmiljøet. Her er de mest almindelige mocks, du sandsynligvis får brug for:
// jest.setup.js — global opsætning af mocks
// Mock af AsyncStorage
jest.mock('@react-native-async-storage/async-storage', () =>
require('@react-native-async-storage/async-storage/jest/async-storage-mock')
);
// Mock af react-native-reanimated
jest.mock('react-native-reanimated', () => {
const Reanimated = require('react-native-reanimated/mock');
Reanimated.default.call = () => {};
return Reanimated;
});
// Mock af navigation
jest.mock('@react-navigation/native', () => ({
...jest.requireActual('@react-navigation/native'),
useNavigation: () => ({
navigate: jest.fn(),
goBack: jest.fn(),
}),
useRoute: () => ({
params: {},
}),
}));
// Test med API-mocking
import { render, screen, waitFor } from '@testing-library/react-native';
import { ProductList } from '../ProductList';
import * as api from '../../services/api';
jest.mock('../../services/api');
const mockedApi = api as jest.Mocked<typeof api>;
describe('ProductList', () => {
it('viser produkter efter indlæsning', async () => {
mockedApi.getProducts.mockResolvedValueOnce([
{ id: '1', name: 'React Native i praksis', price: 299 },
{ id: '2', name: 'Expo for begyndere', price: 199 },
]);
render(<ProductList />);
// Vent på at produkterne vises
await waitFor(() => {
expect(screen.getByText('React Native i praksis')).toBeTruthy();
expect(screen.getByText('Expo for begyndere')).toBeTruthy();
});
});
it('viser fejlmeddelelse ved netværksfejl', async () => {
mockedApi.getProducts.mockRejectedValueOnce(new Error('Netværksfejl'));
render(<ProductList />);
await waitFor(() => {
expect(screen.getByText(/kunne ikke indlæses/i)).toBeTruthy();
});
});
});
Del 3: End-to-end test
Detox — grey-box E2E-testing for React Native
Detox fra Wix er nok det mest populære E2E-testframework til React Native. Det kører som en "grey-box" tester, hvilket vil sige at det har indsigt i appens interne tilstand og automatisk synkroniserer med animationer, netværksanmodninger og andre asynkrone operationer. Det lyder måske lidt abstrakt, men i praksis betyder det, at dine tests sjældent fejler på grund af timing-problemer.
Opsætning af Detox
# Installation
npm install --save-dev detox
npm install -g detox-cli
# Initialiser Detox-konfiguration
detox init
// .detoxrc.js
module.exports = {
testRunner: {
args: {
config: 'e2e/jest.config.js',
},
jest: {
setupTimeout: 120000,
},
},
apps: {
'ios.debug': {
type: 'ios.app',
binaryPath: 'ios/build/Build/Products/Debug-iphonesimulator/MinApp.app',
build: 'xcodebuild -workspace ios/MinApp.xcworkspace -scheme MinApp -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build',
},
'android.debug': {
type: 'android.apk',
binaryPath: 'android/app/build/outputs/apk/debug/app-debug.apk',
build: 'cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug',
},
},
devices: {
simulator: {
type: 'ios.simulator',
device: { type: 'iPhone 16' },
},
emulator: {
type: 'android.emulator',
device: { avdName: 'Pixel_7_API_35' },
},
},
configurations: {
'ios.sim.debug': {
device: 'simulator',
app: 'ios.debug',
},
'android.emu.debug': {
device: 'emulator',
app: 'android.debug',
},
},
};
Skrivning af Detox-tests
// e2e/login.test.js
import { by, device, element, expect } from 'detox';
describe('Login-flow', () => {
beforeAll(async () => {
await device.launchApp();
});
beforeEach(async () => {
await device.reloadReactNative();
});
it('bør vise login-skærmen ved opstart', async () => {
await expect(element(by.text('Velkommen'))).toBeVisible();
await expect(element(by.id('email-input'))).toBeVisible();
await expect(element(by.id('password-input'))).toBeVisible();
});
it('bør vise fejl ved ugyldige legitimationsoplysninger', async () => {
await element(by.id('email-input')).typeText('[email protected]');
await element(by.id('password-input')).typeText('forkertKode');
await element(by.id('login-button')).tap();
await expect(element(by.text('Login mislykkedes'))).toBeVisible();
});
it('bør navigere til dashboard ved succesfuldt login', async () => {
await element(by.id('email-input')).typeText('[email protected]');
await element(by.id('password-input')).typeText('korrektKode123');
await element(by.id('login-button')).tap();
await waitFor(element(by.text('Dashboard')))
.toBeVisible()
.withTimeout(5000);
await expect(element(by.id('welcome-message'))).toBeVisible();
});
it('bør logge ud korrekt', async () => {
// Log ind først
await element(by.id('email-input')).typeText('[email protected]');
await element(by.id('password-input')).typeText('korrektKode123');
await element(by.id('login-button')).tap();
await waitFor(element(by.text('Dashboard')))
.toBeVisible()
.withTimeout(5000);
// Log ud
await element(by.id('menu-button')).tap();
await element(by.text('Log ud')).tap();
// Verificér at vi er tilbage på login-skærmen
await expect(element(by.id('email-input'))).toBeVisible();
});
});
Maestro — det simple alternativ
Maestro er et nyere open source-framework til UI-testing, og det fokuserer på to ting: enkelhed og hastighed. Hvor Detox kræver en del opsætning, bruger Maestro en YAML-baseret deklarativ syntaks, der er så simpel, at selv folk uden programmeringserfaring kan skrive tests. Og det kræver ingen ændringer i din app-kodebase — du kan komme i gang på få minutter.
Opsætning af Maestro
# Installation på macOS
curl -Ls "https://get.maestro.mobile.dev" | bash
# Verificér installationen
maestro --version
Skrivning af Maestro-tests
# e2e/login-flow.yaml
appId: com.minapp
---
- launchApp
# Test af login-flow
- assertVisible: "Velkommen"
- tapOn:
id: "email-input"
- inputText: "[email protected]"
- tapOn:
id: "password-input"
- inputText: "korrektKode123"
- tapOn: "Log ind"
- assertVisible: "Dashboard"
- assertVisible:
id: "welcome-message"
# e2e/product-search.yaml
appId: com.minapp
---
- launchApp
# Naviger til produktsiden
- tapOn: "Produkter"
# Søg efter et produkt
- tapOn:
id: "search-input"
- inputText: "React Native"
# Verificér søgeresultater
- assertVisible: "React Native i praksis"
# Åbn produktdetaljer
- tapOn: "React Native i praksis"
- assertVisible: "Tilføj til kurv"
# Tilføj til kurv
- tapOn: "Tilføj til kurv"
- assertVisible: "Tilføjet!"
# Kør Maestro-tests
maestro test e2e/login-flow.yaml
maestro test e2e/ # Kør alle tests i mappen
Hvornår skal du vælge Detox vs. Maestro?
Det korte svar: det kommer an på dine behov.
- Vælg Detox når du har brug for dyb integration med React Native, automatisk synkronisering med asynkrone operationer, og fuld kontrol over testmiljøet.
- Vælg Maestro når du vil have hurtig opsætning, YAML-baserede tests der er nemme at vedligeholde, og tværplatforms-support (det dækker også Flutter og native apps).
Del 4: Best practices og praktiske tips
Organisering af tests
En velorganiseret teststruktur gør det meget lettere at vedligeholde og finde tests. Her er en struktur, der fungerer godt i praksis:
src/
├── components/
│ ├── LoginForm.tsx
│ └── __tests__/
│ └── LoginForm.test.tsx
├── hooks/
│ ├── useCounter.ts
│ └── __tests__/
│ └── useCounter.test.ts
├── utils/
│ ├── formatCurrency.ts
│ └── __tests__/
│ └── formatCurrency.test.ts
├── services/
│ ├── api.ts
│ └── __tests__/
│ └── api.test.ts
e2e/
├── login.test.js # Detox
├── login-flow.yaml # Maestro
└── jest.config.js
Debugging-checkliste
Når du rammer en fejl i din React Native-app (og det gør vi alle), kan denne systematiske tilgang spare dig for en del frustrationer:
- Reproducer fejlen: Find de præcise trin, der udløser problemet, og skriv dem ned.
- Tjek konsollen: Åbn React Native DevTools og kig i Console-panelet for fejlmeddelelser og advarsler.
- Inspicér netværket: Brug Network-panelet til at tjekke, om API-kald sender de rigtige data og får de forventede svar.
- Sæt breakpoints: Placer breakpoints strategisk i den kode, der er relateret til fejlen, og trin igennem udførelsen.
- Tjek state: Brug Components-panelet eller Reactotron til at inspicere komponenternes state og props.
- Isolér problemet: Brug
console.log()eller logpoints til at indsnævre, hvor fejlen opstår. - Skriv en test: Når fejlen er fundet og rettet, skriv en test der fanger netop denne fejl — så undgår du regression fremover.
Performance-debugging med Profiler
React Profiler i React Native DevTools er uundværlig, når du skal finde unødvendige re-renders og optimere din apps performance. Her er et hurtigt eksempel:
// Brug React.memo til at undgå unødvendige re-renders
import React, { memo } from 'react';
import { View, Text } from 'react-native';
interface ProductCardProps {
name: string;
price: number;
}
export const ProductCard = memo<ProductCardProps>(({ name, price }) => {
console.log(`ProductCard rendereret: ${name}`); // Brug til at spore renders
return (
<View>
<Text>{name}</Text>
<Text>{price} kr.</Text>
</View>
);
});
// Test at memo virker korrekt
// components/__tests__/ProductCard.test.tsx
import { render } from '@testing-library/react-native';
import { ProductCard } from '../ProductCard';
describe('ProductCard', () => {
it('renderer produktinfo korrekt', () => {
const { getByText } = render(
<ProductCard name="Testprodukt" price={199} />
);
expect(getByText('Testprodukt')).toBeTruthy();
expect(getByText('199 kr.')).toBeTruthy();
});
});
CI/CD-integration af tests
For at sikre løbende kvalitet bør du integrere dine tests i din CI/CD-pipeline. Her er et eksempel med GitHub Actions, der dækker både unit tests og E2E-tests:
# .github/workflows/test.yml
name: React Native Tests
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm test -- --coverage --ci
- uses: codecov/codecov-action@v4
with:
file: ./coverage/lcov.info
e2e-ios:
runs-on: macos-latest
needs: unit-tests
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: brew tap wix/brew && brew install applesimutils
- run: npx detox build --configuration ios.sim.release
- run: npx detox test --configuration ios.sim.release --cleanup
Konklusion
Debugging og test af React Native-apps i 2026 er ærligt talt mere tilgængeligt end nogensinde. Med React Native DevTools som det nye standardværktøj, Radon IDE der bringer debugging direkte ind i editoren, og modne testframeworks som Jest, RNTL, Detox og Maestro har du alt, hvad du behøver for at bygge pålidelige mobilapps.
Her er de vigtigste ting at tage med fra denne guide:
- React Native DevTools er nu standarden for debugging og giver en Chrome DevTools-lignende oplevelse
- Radon IDE giver dig en integreret debugging-oplevelse direkte i VSCode og Cursor
- Jest og React Native Testing Library er fundamentet for unit- og komponenttests med fokus på brugerinteraktioner
- Detox er det oplagte valg til E2E-test med dyb React Native-integration
- Maestro giver dig en simpel YAML-baseret tilgang til E2E-test med minimal opsætning
- En lagdelt teststrategi med mange unit tests, målrettede komponenttests og strategiske E2E-tests giver den bedste balance mellem hastighed og dækning
Mit råd? Start med unit tests for din forretningslogik. Tilføj derefter komponenttests for dine vigtigste UI-komponenter. Og afslut med E2E-tests for de kritiske brugerflows. Med den tilgang opdager du fejl tidligt, reducerer regressionsfejl og leverer i sidste ende en bedre app til dine brugere.