Skip to content

Latest commit

 

History

History
142 lines (98 loc) · 3.69 KB

README.md

File metadata and controls

142 lines (98 loc) · 3.69 KB

op-test

A lightweight, in-app testing framework for React Native applications.

Motivation

You can think of op-test as a half-way between a Detox (UI e2e test) and Jest (runs in a Node environment). It's specially useful for library authors that depend on native libraries or some sort of runtime behavior that requires a simulator/emulator/device to assert correct functionality. It's specially suited to test JSI enabled libraries as testing JSI/TurboModules/NitroModules functions require the whole the React Native app to have started.

Screenshot of test results

Installation

npm install --save @op-engineering/op-test

Usage

Import the testing utilities and write your tests:

import { describe, it, expect } from '@op-engineering/op-test';

describe('Example', () => {
  it('should work', () => {
    expect(1 + 1).toBe(2);
  });
});

You can then call the runTests function which will run all the functions registered under the describe blocks:

import {runTests} from '@op-engineering/op-test`;

const results = await runTests()

Available Functions

  • describe(name: string, fn: () => void): Group related tests
  • it(name: string, fn: TestFunction): Define a test case
  • expect(value): Create assertions with:
    • .toBe(expected): Strict equality
    • .toEqual(expected): Loose equality
    • .not.toBe(expected): Negative strict equality
    • .not.toEqual(expected): Negative loose equality
    • .resolves: For testing promises
    • .rejects: For testing promise rejections

Hooks

  • beforeAll(fn): Run before all tests in a describe block
  • beforeEach(fn): Run before each test
  • afterAll(fn): Run after all tests in a describe block
  • afterEach(fn): Run after each test

Display Results

Use the displayResults component to show test results in your app in a visual way:

import { runTests, displayResults } from '@op-engineering/op-test';

export default function App() {
  const [result, setResult] = useState(null);

  useEffect(() => {
    runTests().then(setResult);
  }, []);

  return displayResults(result);
}

CI

You probably want to automate your in-app tests in your CI. For this you can pass the DescribeBlock to the allTestsPassed function, it will return a boolean that indicates if all tests have passed. You can this automate this in various ways in your CI. My favorite is starting and in-app http server, exposing a single endpoint. From your CI you can then start the app, query this endpoint (over and over until it returns a reponse). Here is a snippet how to do this.

Server code:

import { BridgeServer } from 'react-native-http-bridge-refurbished';

let testsPassed: boolean | null = null;

const server = new BridgeServer('http_service', true);

server.get('/ping', async (_req, _res) => {
  return { message: 'pong' };
});

server.get('/results', async (_req, _res) => {
  return { testPassed };
});

server.listen(9000);

export function startServer() {
  return server;
}

export function stopServer() {
  server.stop();
}

export function setServerTestPassed(r: boolean) {
  testsPassed = r;
}

Then to run your tests:

import {
  runTests,
  displayResults,
  allTestsPassed,
} from '@op-engineering/op-test';
import { startServer, setServerTestPassed } from './server';

export default function App() {
  const [result, setResult] = useState(null);

  useEffect(() => {
    runTests().then((newResults) => {
      setServerTestPassed(allTestsPassed(newResults));
      setResult(newResults);
    });

    startServer();
  }, []);

  return displayResults(result);
}

The CI code is left as an exercise to the reader, just curl the device ip until you get a response.

License

MIT