While JavaScript logic can be tested easily with Jest, testing native modules and platform-specific functionality has always been challenging. Let's understand why and how React Native Harness solves it.
Jest tests run in Node.js environment, which means they have no access to native modules or device capabilities:
Testing this code would require mocking the whole NativeModules.DeviceInfo object, which defeats the purpose of testing this code.
End-to-end testing tools run in real environments with native environment access, but they require cumbersome UI automation. You need to implement some sort of UI that allows you to interact with your native module and execute actions. Then, you need to somehow expose results so you can check them and verify they are correct. This is doable, but not the best developer experience as you need to write and maintain more code than necessary.
This approach requires complex test scenarios for simple logic and makes it difficult to isolate what you're actually testing.
What we really want is to run the same tests we write for our apps (or with minimal changes) directly on a device where native modules are available:
We should not be forced to implement any UI. We should be able to write our tests as we do in Jest, and they should execute on a device.
JavaScript code needs to be bundled by Metro to run on real devices with the Hermes engine. This means we need to:
This is exactly how React Native Harness works.
React Native Harness uses your existing Metro bundler to create a device-compatible bundle that includes:
.harness.js/.harness.ts)Instead of bundling your normal app, it bundles a test runtime that can execute tests on the device.
The CLI installs this test bundle on your target iOS simulator or Android emulator. When the app launches, instead of your normal app UI, the Harness test runner takes control and:
describe, it, expect)As tests execute on the device, the Harness runtime sends results back to the CLI running in Node.js in real-time. You see the same familiar test output you'd expect from Jest.
This architecture gives you:
Jest's Familiar APIs: Write tests using the same describe, it, and expect syntax you already know, with all the lifecycle hooks and test organization patterns you're familiar with.
Real Native Environment: Tests run on actual iOS simulators and Android emulators with full access to native modules, platform APIs, and device capabilities—no mocking required.
Practical Development: No complex E2E setup, no UI automation, no brittle selectors. Just write tests that directly call the APIs you want to test.
Before React Native Harness, testing a simple device info call required:
With React Native Harness: Write a simple JavaScript test that calls the real API and asserts the result. Done.
Now that you understand why React Native Harness exists and how it solves the native testing problem, let's get it set up in your project.