Simple and complete React Native testing utilities that encourage good testing practices
—
Global configuration options and setup utilities for customizing React Native Testing Library behavior across your test suite, including async timeouts, debugging options, and rendering modes.
Configure library-wide settings that affect all tests in your suite.
/**
* Configure global options for React Native Testing Library
* @param options - Configuration options to apply
*/
function configure(options: Partial<Config & ConfigAliasOptions>): void;
/**
* Reset all configuration to default values
*/
function resetToDefaults(): void;
/**
* Get current configuration settings
* @returns Current configuration object
*/
function getConfig(): Config;
interface Config {
/** Default timeout in milliseconds for waitFor and findBy queries */
asyncUtilTimeout: number;
/** Default value for includeHiddenElements query option */
defaultIncludeHiddenElements: boolean;
/** Default options for debug helper function */
defaultDebugOptions?: Partial<DebugOptions>;
/** Enable/disable concurrent rendering by default */
concurrentRoot: boolean;
}
interface ConfigAliasOptions {
/** Alias for defaultIncludeHiddenElements (RTL compatibility) */
defaultHidden: boolean;
}
interface DebugOptions {
/** Function to transform props before displaying */
mapProps?: (props: Record<string, any>) => Record<string, any>;
/** Maximum length of debug output */
maxLength?: number;
}Usage Examples:
import { configure, resetToDefaults, getConfig } from "@testing-library/react-native";
describe("configuration examples", () => {
afterEach(() => {
// Reset to defaults after each test
resetToDefaults();
});
test("async timeout configuration", async () => {
// Set longer timeout for slow components
configure({ asyncUtilTimeout: 5000 });
const SlowComponent = () => {
const [loaded, setLoaded] = useState(false);
useEffect(() => {
setTimeout(() => setLoaded(true), 3000);
}, []);
return loaded ? <Text>Loaded</Text> : <Text>Loading...</Text>;
};
render(<SlowComponent />);
// This will use the configured 5 second timeout
await screen.findByText("Loaded");
// Verify configuration was applied
expect(getConfig().asyncUtilTimeout).toBe(5000);
});
test("hidden elements configuration", () => {
// Include hidden elements by default
configure({ defaultIncludeHiddenElements: true });
render(
<View>
<Text>Visible text</Text>
<Text style={{ opacity: 0 }}>Hidden text</Text>
<View accessibilityElementsHidden>
<Text>Accessibility hidden</Text>
</View>
</View>
);
// These queries will now include hidden elements by default
const allTexts = screen.getAllByText(/text/);
expect(allTexts.length).toBeGreaterThan(1);
// Can still override per query
const visibleOnly = screen.getAllByText(/text/, {
includeHiddenElements: false
});
expect(visibleOnly.length).toBeLessThan(allTexts.length);
});
test("debug options configuration", () => {
// Configure debug output
configure({
defaultDebugOptions: {
maxLength: 500,
mapProps: (props) => ({
...props,
// Hide functions in debug output
onPress: props.onPress ? "[Function]" : undefined,
style: "[Style Object]"
})
}
});
const MyComponent = () => (
<Pressable
onPress={() => {}}
style={{ backgroundColor: "blue" }}
>
<Text>Debug me</Text>
</Pressable>
);
const { debug } = render(<MyComponent />);
// Debug will use configured options
debug(); // Output will show "[Function]" instead of actual function
});
test("concurrent rendering configuration", () => {
// Disable concurrent rendering globally
configure({ concurrentRoot: false });
const AsyncComponent = () => {
const [value, setValue] = useState("initial");
useEffect(() => {
setValue("updated");
}, []);
return <Text>{value}</Text>;
};
// This will use legacy rendering mode
render(<AsyncComponent />);
expect(screen.getByText("updated")).toBeOnTheScreen();
expect(getConfig().concurrentRoot).toBe(false);
});
test("RTL compatibility aliases", () => {
// Using RTL-style alias
configure({ defaultHidden: true });
// Should set defaultIncludeHiddenElements
expect(getConfig().defaultIncludeHiddenElements).toBe(true);
});
});Configuration patterns for different test environments and frameworks.
/**
* Environment-specific setup utilities
*/Usage Examples:
// jest.config.js or test setup file
import { configure } from "@testing-library/react-native";
// Global test suite configuration
configure({
// Longer timeout for integration tests
asyncUtilTimeout: 2000,
// Include hidden elements for comprehensive testing
defaultIncludeHiddenElements: false,
// Enable concurrent rendering for modern React features
concurrentRoot: true,
// Clean debug output
defaultDebugOptions: {
maxLength: 1000,
mapProps: (props) => {
const cleaned = { ...props };
// Hide complex objects for cleaner output
Object.keys(cleaned).forEach(key => {
if (typeof cleaned[key] === "function") {
cleaned[key] = "[Function]";
} else if (key === "style" && typeof cleaned[key] === "object") {
cleaned[key] = "[Style]";
}
});
return cleaned;
}
}
});
// Project-specific setup
test("project configuration", () => {
// Configuration for React Native Paper components
configure({
defaultDebugOptions: {
mapProps: (props) => ({
...props,
// Paper-specific props cleanup
theme: props.theme ? "[Theme]" : undefined,
contentStyle: props.contentStyle ? "[Style]" : undefined
})
}
});
const PaperButton = () => (
<Button mode="contained" onPress={() => {}}>
Paper Button
</Button>
);
const { debug } = render(<PaperButton />);
debug(); // Clean output without theme object details
});Configuration for automatic test cleanup behavior.
/**
* Auto-cleanup configuration via environment variables and imports
*/
// Standard import with auto-cleanup
import { render } from "@testing-library/react-native";
// Pure import without auto-cleanup
import { render } from "@testing-library/react-native/pure";
// Environment variable to disable auto-cleanup
// Set RNTL_SKIP_AUTO_CLEANUP=true to disable automatic cleanupUsage Examples:
// Standard usage with auto-cleanup (default)
describe("auto-cleanup enabled", () => {
test("first test", () => {
render(<Text>First test</Text>);
expect(screen.getByText("First test")).toBeOnTheScreen();
// Automatic cleanup happens after this test
});
test("second test", () => {
// Screen is clean from previous test
render(<Text>Second test</Text>);
expect(screen.getByText("Second test")).toBeOnTheScreen();
expect(screen.queryByText("First test")).not.toBeOnTheScreen();
});
});
// Manual cleanup control
describe("manual cleanup control", () => {
// Import pure version
const { render, cleanup } = require("@testing-library/react-native/pure");
afterEach(() => {
// Manual cleanup after each test
cleanup();
});
test("controlled cleanup", () => {
render(<Text>Controlled test</Text>);
expect(screen.getByText("Controlled test")).toBeOnTheScreen();
// Could do additional cleanup here if needed
cleanup();
// Screen is now clean
expect(screen.queryByText("Controlled test")).not.toBeOnTheScreen();
});
});
// Environment variable configuration
describe("environment-based cleanup", () => {
const originalEnv = process.env.RNTL_SKIP_AUTO_CLEANUP;
afterAll(() => {
// Restore original environment
if (originalEnv !== undefined) {
process.env.RNTL_SKIP_AUTO_CLEANUP = originalEnv;
} else {
delete process.env.RNTL_SKIP_AUTO_CLEANUP;
}
});
test("disable auto-cleanup via environment", () => {
// This would typically be set in test environment configuration
process.env.RNTL_SKIP_AUTO_CLEANUP = "true";
// Re-import would be needed to pick up env change
// In practice, this is set before importing the library
render(<Text>No auto cleanup</Text>);
// Manual cleanup required when env var is set
});
});Advanced debugging configuration for test development and troubleshooting.
/**
* Debug configuration options
*/
interface DebugOptions {
/** Transform props before displaying */
mapProps?: (props: Record<string, any>) => Record<string, any>;
/** Maximum length of debug output */
maxLength?: number;
}
/**
* Debug function type
*/
interface DebugFunction {
(element?: ReactTestInstance, maxLength?: number, options?: DebugOptions): void;
}Usage Examples:
test("debug configuration examples", () => {
// Component with complex props
const ComplexComponent = () => (
<View>
<Pressable
onPress={() => console.log("pressed")}
style={{
backgroundColor: "blue",
padding: 10,
borderRadius: 5
}}
testID="complex-button"
>
<Text>Complex Button</Text>
</Pressable>
<FlatList
data={[1, 2, 3]}
renderItem={({ item }) => <Text key={item}>Item {item}</Text>}
keyExtractor={(item) => item.toString()}
/>
</View>
);
const { debug } = render(<ComplexComponent />);
// Default debug output (might be verbose)
debug();
// Limited length debug
debug(undefined, 200);
// Custom props mapping
debug(undefined, undefined, {
mapProps: (props) => ({
...props,
// Simplify complex props
onPress: props.onPress ? "[Function]" : undefined,
style: props.style ? "[Style Object]" : undefined,
data: props.data ? `[Array of ${props.data.length}]` : undefined,
renderItem: props.renderItem ? "[Render Function]" : undefined
})
});
// Debug specific element
const button = screen.getByTestId("complex-button");
debug(button, 500, {
mapProps: (props) => ({
testID: props.testID,
onPress: "[Function]",
style: "[Simplified]"
})
});
});
test("debug configuration per test suite", () => {
// Suite-specific debug configuration
const originalConfig = getConfig();
configure({
defaultDebugOptions: {
maxLength: 300,
mapProps: (props) => {
// Project-specific prop cleaning
return Object.keys(props).reduce((clean, key) => {
if (key.startsWith("accessibility")) {
clean[key] = props[key];
} else if (typeof props[key] === "function") {
clean[key] = `[${key}Function]`;
} else if (key === "children" && Array.isArray(props[key])) {
clean[key] = `[${props[key].length} children]`;
} else {
clean[key] = props[key];
}
return clean;
}, {});
}
}
});
const TestComponent = () => (
<View accessibilityLabel="Test container">
<Text>Child 1</Text>
<Text>Child 2</Text>
<Pressable onPress={() => {}} accessibilityRole="button">
<Text>Button</Text>
</Pressable>
</View>
);
const { debug } = render(<TestComponent />);
// Uses configured debug options
debug();
// Restore original config
configure(originalConfig);
});Configuration options for optimizing test performance.
/**
* Performance-related configuration
*/Usage Examples:
describe("performance configuration", () => {
test("optimized for fast tests", () => {
configure({
// Shorter timeout for unit tests
asyncUtilTimeout: 500,
// Disable concurrent rendering for predictable timing
concurrentRoot: false,
// Minimal debug output
defaultDebugOptions: {
maxLength: 100,
mapProps: (props) => ({ testID: props.testID })
}
});
const FastComponent = () => {
const [value, setValue] = useState("initial");
useEffect(() => {
// Fast update
setTimeout(() => setValue("updated"), 10);
}, []);
return <Text testID="fast-text">{value}</Text>;
};
render(<FastComponent />);
// Fast assertion with short timeout
return screen.findByText("updated");
});
test("optimized for integration tests", () => {
configure({
// Longer timeout for complex interactions
asyncUtilTimeout: 10000,
// Enable concurrent rendering for realistic testing
concurrentRoot: true,
// Include hidden elements for comprehensive testing
defaultIncludeHiddenElements: true,
// Detailed debug output for troubleshooting
defaultDebugOptions: {
maxLength: 2000
}
});
const IntegrationComponent = () => {
const [loading, setLoading] = useState(true);
const [data, setData] = useState(null);
useEffect(() => {
// Simulate slow API call
setTimeout(() => {
setData("Integration data");
setLoading(false);
}, 1000);
}, []);
return loading ? (
<Text>Loading integration test...</Text>
) : (
<Text testID="integration-result">{data}</Text>
);
};
render(<IntegrationComponent />);
// Long timeout for integration test
return screen.findByTestId("integration-result");
});
test("memory-conscious configuration", () => {
configure({
// Compact debug output to reduce memory usage
defaultDebugOptions: {
maxLength: 50,
mapProps: () => ({ "[props]": "..." }) // Minimal prop display
}
});
// Large component tree
const LargeComponent = () => (
<View>
{Array.from({ length: 100 }, (_, i) => (
<Text key={i} testID={`item-${i}`}>
Item {i}
</Text>
))}
</View>
);
const { debug } = render(<LargeComponent />);
// Compact debug output
debug();
// Test still works normally
expect(screen.getByTestId("item-0")).toBeOnTheScreen();
expect(screen.getByTestId("item-99")).toBeOnTheScreen();
});
});Configuration that adapts to different testing environments.
/**
* Environment-aware configuration
*/Usage Examples:
// Test environment detection and configuration
describe("environment-aware configuration", () => {
test("CI environment configuration", () => {
const isCI = process.env.CI === "true";
configure({
// Longer timeouts in CI environments
asyncUtilTimeout: isCI ? 10000 : 2000,
// Disable concurrent rendering in CI for stability
concurrentRoot: !isCI,
// More verbose debugging in CI
defaultDebugOptions: {
maxLength: isCI ? 5000 : 1000
}
});
const EnvironmentComponent = () => {
const [env, setEnv] = useState("unknown");
useEffect(() => {
// Simulate environment detection
setTimeout(() => {
setEnv(isCI ? "CI" : "local");
}, isCI ? 500 : 100);
}, []);
return <Text testID="env-indicator">Environment: {env}</Text>;
};
render(<EnvironmentComponent />);
// Timeout will be appropriate for environment
return screen.findByText(/Environment: (CI|local)/);
});
test("jest environment configuration", () => {
const isJest = typeof jest !== "undefined";
if (isJest) {
configure({
defaultDebugOptions: {
mapProps: (props) => ({
...props,
// Jest-specific prop handling
onPress: props.onPress ? "[Jest Mock Function]" : undefined
})
}
});
}
const JestComponent = () => {
const mockFn = isJest ? jest.fn() : () => {};
return (
<Pressable onPress={mockFn} testID="jest-button">
<Text>Jest Button</Text>
</Pressable>
);
};
const { debug } = render(<JestComponent />);
if (isJest) {
debug(); // Will show "[Jest Mock Function]"
}
expect(screen.getByTestId("jest-button")).toBeOnTheScreen();
});
test("development vs production configuration", () => {
const isDev = process.env.NODE_ENV !== "production";
configure({
defaultDebugOptions: {
maxLength: isDev ? 2000 : 500,
mapProps: isDev
? undefined // Full props in development
: (props) => ({ testID: props.testID }) // Minimal in production
}
});
const DevComponent = () => (
<View
testID="dev-component"
style={{ padding: 10 }}
accessibilityLabel="Development component"
>
<Text>Development Component</Text>
</View>
);
const { debug } = render(<DevComponent />);
// Debug verbosity depends on environment
debug();
expect(screen.getByTestId("dev-component")).toBeOnTheScreen();
});
});Install with Tessl CLI
npx tessl i tessl/npm-testing-library--react-native