
こんにちは、株式会社カミナシのエンジニア @imuです。
はじめに
みなさんのプロダクトでテスト書いてますか?
『カミナシ』はオフライン機能を提供しており、ローカルデータベースはSQLite(expo-sqlite)を利用しています。
オフライン機能って何?という話は、こちらの記事をご参照ください。
kaminashi-developer.hatenablog.jp
SQLiteに保存されるデータは『カミナシ』にとって重要なデータが保存されています。 そのため万が一にでもデータを欠損してはいけないため、テストを充実させていきたいと思っています。
まずはCRUD処理を簡単に書いてみようと思ったのですが、SQLite(expo-sqlite)+ TypeORMで接続のテストが書けずに困った話をしようと思います。(これを解決出来たらCRUD処理を書きたい)
もし解決策を知っている方がいたら教えていただけると幸いです。
↓↓↓ 以下は試した環境を書いています ↓↓↓
前準備@インストール編
expoをインストール
npm install -g expo-cli
テンプレートを作成
今回はManaged workflowのblank(TypeScript)を利用します。
expo int sqlite_test

しばらくするとプロジェクトが作成されます!

SQLiteをインストール
expo-sqliteでreact-native-sqlite-storageを利用しているのでインストールします。
yarn add expo-sqlite@~8.5.0 react-native-sqlite-storage@5.0.0
TypeScriptのORマッパー
SQLiteのDB操作をするためにTypeORMを利用します。
yarn add github:cuibonobo/typeorm-package reflect-metadata --dev
tsconfig.json で以下の設定を有効にします。
"emitDecoratorMetadata": true "experimentalDecorators": true
テストフレームワークのインストール
Expoガイドに載っているJestを利用します。
yarn add jest-expo @types/jest --dev
テストファイルには以下のインポートを追加しましょう。(TypeORMを使わない場合は不要です)
hoge.test.ts
import 'reflect-metadata'
package.jsonに以下の設定を追加してください。
"scripts": { ... "test": "jest" }, "jest": { "preset": "jest-expo" }
前準備@データベース作成編
TypeORMを使ってユーザーテーブルのEntityを作成
注意したいのがSQLiteを利用するので、Columnに指定するtypeがSQLiteの定義になっていることを確認してください。定義が間違っている場合はwarningになってしまいます。
sqlite/entities/user.ts
import { Entity, PrimaryColumn, Column, CreateDateColumn } from 'typeorm'
@Entity()
export class EntityTestUser {
// Primaryの数値型
@PrimaryColumn({ type: 'integer' })
id?: number
// 100文字まで保存
@Column({ type: 'text', length: 100 })
name?: string
// 数値型でNULLを許可する
@Column({ type: 'integer', nullable: true })
age?: number
// 作成日の初期値はdefaultで予め定義しておきます
@CreateDateColumn({ name: 'created_at', precision: 0, default: () => `DATETIME('now', 'localtime')` })
readonly createdAt?: string
}
データベース接続処理を作成
Entityを作成したあとは、データベースの接続設定を行いましょう。
今回はユニットテストで使うため、databaseは:memory:とします。
sqlite/types/database.ts
// データベース名 export const DATABASE_NAME = ':memory:' // コネクション名 export const CONNECTION_NAME = 'sample'
sqlite/connection.ts
import { createConnection } from 'typeorm'
import { EntityTestUser } from './entities/user'
import { DATABASE_NAME, CONNECTION_NAME } from './types/database'
const createConnectionSQLite = async () => {
return await createConnection({
database: DATABASE_NAME,
name: CONNECTION_NAME,
// expoのsqliteを利用する
type: 'expo',
driver: require('expo-sqlite'),
// 利用するテーブル一覧(entitiesに追加しないと対象とならない点に注意してください)
entities: [EntityTestUser],
// createConnectionしたときにentitiesのテーブルを作成する(falseの場合は自動生成しません)
synchronize: true,
// queryのログを出力
logging: ['query']
})
}
export default createConnectionSQLite
この例ではexpo-sqliteドライバを利用したexpoデータベースを利用します。
別のデータベースを利用する場合はtypeをmysql, postgres, react-native等を利用できます。その際は各データベースの設定にホストやポート等を変更してください。
SQLiteのテスト書いてみよう
これで前準備はバッチリですね!早速テストを書いてみましょう!
sqlite/connection.test.ts
import createConnectionSQLite from './connection'
import 'reflect-metadata'
describe('expo-sqlite testing', () => {
test('exist connection', async () => {
const connection = await createConnectionSQLite()
expect(connection).not.toBe(undefined)
})
})
接続が出来ていればテストが成功するので、yarn test を実行してみましょう!
% yarn test
yarn run v1.19.1
$ jest
FAIL sqlite/connection.test.ts
expo-sqlite testing
✕ exist connection (362ms)
● expo-sqlite testing › exist connection
TypeError: Cannot read property 'map' of undefined
at node_modules/expo-sqlite/src/SQLite.ts:27:41
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 3.073s
Ran all test suites.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
ムムム…。華麗にコケました。
エラー箇所のコードを確認したところ、expo-sqlite の SQLiteDatabase.exec でエラーになっていました。
thenで正常に終わっているが、nativeResultSetsに入っていないようです。

ここから数時間近くハマって解決策が見つかっていません...。
確認したことは
- createConnectionのプロパティでdatabaseにmemoryが利用できないのか
- App.tsxに記述して正常に動作しているため、memoryの問題ではなかった
- この関数自体は正常に動作している
- TypeORM, ExpoのGithubのissue
- 似たようなissueがあったけどenumは使っていなかった github.com
もしかして、Jestのテストで利用出来ないのでは?と思い、素のSQLiteを使ってみました。
まずはSQLite接続用のドライバーが別途必要なためインストールを行います。
yarn add sqlite3 --dev
sqlite/connection.ts
const createConnectionSQLite = async () => {
return await createConnection({
database: DATABASE_NAME,
name: CONNECTION_NAME,
type: 'sqlite',
entities: [EntityTestUser],
synchronize: true
})
}
再度 yarn test を実行してみましょう!
% yarn test
yarn run v1.19.1
$ jest
PASS sqlite/connection.test.ts
expo-sqlite testing
✓ exist connection (311ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 4.739s
Ran all test suites.
✨ Done in 5.89s.
テストが通りました!
SQLiteの問題ではなく、expo-sqliteを使ったテストが動かないということだと思います。これが環境問題なのか、設定不足なのかが現時点では分かっていない状態です。
分かっていないこと
- TypeORMのcreateConnectionで
expo-sqliteのドライバを使ったテストの書き方が分からない!
解決策を知っている方がいたら、コメント等で教えていただけると幸いです。
おわりに
今回は記事はTypeORM + SQLite(expo-sqlite)のテストが書けなかった話でした。 ホントは出来たことを公開したかったのですが…。
出来ていないことを外部に公開することを躊躇いましたが、弊社のValueに全開オープンがあるので大丈夫なはず!
最後に弊社ではエンジニアを募集しております。 興味がある、話を聞いてみたい、応募したいという方はお気軽にご応募ください!