Loading... Search articles

Search for articles

Sorry, but we couldn't find any matches...

But perhaps we can interest you in one of our more popular articles?
How to test SQLite for React Native apps using Jest

How to test SQLite for React Native apps using Jest

Apr 12, 2021

In native Android, it’s common to test SQLite code in isolation. In this article, we will cover how to test SQLite for React Native apps using Jest.

React Native apps have been gaining popularity since 2015 and are still growing consistently in 2021. With millions of apps migrating to the cross-platform space, it is crucial for developers to test them thoroughly. A huge portion of cross-platform apps also rely on local database updates to provide exclusive features.

Written by Sneh Pandya

Introduction to Jest

Testing allows developers to test the code that they write. One of the main goals of testing is to catch bugs earlier by running the tests locally or through a process. Unit tests can also help developers who may not have worked on the code to understand what the code should be doing when it executes.

Jest is a JavaScript testing library created by the same organization behind React: Facebook. But while Jest was created by the same developers who created React, it is not limited to only React – it can also be used with other frameworks, like Node, Angular, Vue, and Babel.

Set up React Native application

Let’s get started by setting up our project. To create a new application, run the commands as follows:

npm install -g expo-cli // To add Expo CLI on your machine

npx react-native init ReactNativeDBTesting // To set up a new React Native application

cd ReactNativeDBTesting

Next, we will use node-sqlite3 to test sqlite code using jest. It is compatible with packages like react-native-sqlite2 and react-native-sqlite-storage.

Let’s install the dependencies by running the command below in the terminal:

npm install --saveDev sqlite3@4.1.1 @types/websql@0.0.27 @types/sqlite@3.1.6    // SQLite

npm install --save-dev jest     // Jest

npm install --save-dev @testing-library/react-native @testing-library/jest-native

Make sure you are using the same SQLite version in Jest and the application environment.

Once successfully completed, you can verify that Jest has been added in package.json with the configuration:

{
  "scripts": {
    "test": "jest"
  },
  "jest": {
    "preset": "react-native"
  }
}

Set up Jest for React Native

To use Jest, we need to follow a few steps and set up Jest in our React Native application.

  1. In the root directory of the project, add a new file and name it jest.config.js.

  2. Within jest.config.js, add the code below:

module.exports = {
  preset: 'react-native',
  setupFilesAfterEnv: ['@testing-library/jest-native/extend-expect'],
};

present is a preset that is used as a base for Jest’s configuration. A preset should point to an npm module that has a jest-preset.json or jest-preset.js file at the root.

setUpFilesAfterEnv specifies a list of paths to modules that run some code to configure or set up the testing framework before each test file in the suite is executed.

Define queries for tests

We will extract a query executor from the application’s environment for testing purposes.

export type SqlExecutor = {
  read: (query: string, args: Array<string>) => Promise<string[]>,
  write: (query: string, args: Array<string>) => Promise<string[]>,
}

Now, let’s define queries using the read and write of the executor in createUsers.ts, as shown below:

const CREATE_TABLE_IF_NOT_EXIST = `CREATE TABLE IF NOT EXISTS USERS (NAME TEXT);`

const GET_COUNT = `SELECT count(*) AS result FROM USERS`

export const createUsers = (executor: SqlExecutor) => ({
    createTable: () => executor.write(CREATE_TABLE_IF_NOT_EXIST),
    getCount: () => executor.read(GET_COUNT).then(result => Number.parseInt(result[0]))
})

Writing test for database

Before writing the test, we need to write an implementation of the executor.

const { promisify } = require('util');
import { Database } from 'sqlite3';
const createNodeSqlite2Executor = (db: Database): SqlExecutor => {
  const allPromise = promisify(db.all).bind(db);
  return {
    read: ((query, args) => allPromise(query, args)),
    write: ((query, args) => allPromise(query, args)),
  };
};

The executor returns a Promise with the result of read and write query operations.

Next, let’s add the test configuration for the database, as shown below:

const sqlite3 = require('sqlite3').verbose();
import { createUsers } from "./createUsers"

const inMemoryDb = new sqlite3.Database(':memory:');
const nodeExecutor = createNodeSqlite2Executor(inMemoryDb);
const userQueries = createUsers(nodeExecutor);

describe('usersQueries', () => {
    beforeEach(async () => {
        await userQueries.createTable();
    })
    it('getCount should return zero for empty table', async () => {
        const count = await userQueries.getCount();
        expect(count).toEqual(0);
    });
});

This test will describe the status of the Users table after communicating with the actual local database.

Add configuration to React Native application

After writing the test for our database, let’s implement SqlExtractor in our React Native application.

const parseSql(rs: SQLResultSet): string[] => {
  const result: Array<string> = new Array(rs.rows.length);
  for (let i = 0; i < rs.rows.length; i += 1) {
    result[i] = rs.rows.item(i);
  }
  return result;
}

const executeSql = (
  tr: SQLTransaction,
  query: string,
  ...args: string[]
):Promise<string[]> => new Promise((resolve, reject) => {
  tr.executeSql(
    query,
    args,
    (_, result) => resolve(parseSql(result)),
    (_, error) => {
      reject(error);
      return true;
    },
  );
});

export const createRnSqlite2Executor = (db: Database) : SqlExecutor => ({
  read: (query, ...args: string[]) => new Promise(
    (resolve) => db.readTransaction((tr) => resolve(executeSql(tr, query, ...args))),
  ),
  write: (query, ...args: string[]) => new Promise(
    (resolve) => db.transaction((tr) => resolve(executeSql(tr, query, ...args))),
  ),
});

Now we can use createRnSqlite2Executor in our React Native application (for example, using the React Context API).

Conclusion

We have covered the basics of database testing for SQLite with React Native applications using Jest. This approach can also potentially be used with the Detox framework as well.


Sneh is a Senior Product Manager based in Baroda. He is a community organizer at Google Developers Group and co-host of NinjaTalks podcast. His passion for building meaningful products inspires him to write blogs, speak at conferences and mentor different talents. You can reach out to him over Twitter (@SnehPandya18) or via email (sneh.pandya1@gmail.com).

Latest articles

Show more posts