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.
In the root directory of the project, add a new file and name it
jest.config.js
.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.
Useful links and resources
Here’s an article on React Native Detox tests on Codemagic.
Here’s an article on testing React Native apps with Jest and Codemagic.
Here’s an article on why Android developers should pay attention to React Native in 2021.
Here’s an article on choosing the right database for your React Native app.
Here’s an article on how to improve the performance of a React Native app.
Here’s a practical guide on Continuous Integration and Delivery for React Native apps.
For discussions, learning and support, join the Codemagic Slack Community.
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).