背景
卒業研究でWebフロントエンドで動作する分散型の図書館WebアプリをNuxt.js+TypeScriptで開発しています。このWebアプリではデータベースとしてIPFS上にデータを保管するOrbitDBを採用しており、ブラウザ上で直接P2Pネットワークに参加して接続を確立してDBをやり取りします。OrbitDB/IPFSを利用することで、複数の図書館で共通のデータベースを扱うことができるため、蔵書や貸し出し状態を容易にやり取りでき、いろんな点で面白い/便利なんじゃないかと思いはじめた研究です。
これを作るためには複数のデータベースを扱う必要が出てくるため、大量のDAO、model、Typeを定義しました。これらすべてをブラウザ上でいちいち手動で確認はしていられません。 なので、使うのは初めてですが、Jestを使って テストをしたくなりました。
テストしたい内容
beforeAllの内容
全体テスト前にIPFSインスタンスを作成、IPFSインスタンスを渡してOrbitDBインスタンスを作成
BibliographyDaoを作成、BibliographyDaoを初期化(コンストラクタ無いでasyncが使えなかったのでやむおえず分離)
testの内容
蔵書情報モデルを作成
蔵書情報をOrbitDBに追加
追加結果などログ表示
実際テストしてみた
ReferenceError: TextDecoder is not defined.
と言われて動かなかった。検索したところ既にissueと記事があった。jsdomじゃ組み込みのTextDecoderが無いよっていう話らしい。
どうやら setupJest.ts
とやらを作って jest起動時に細工をすればいいらしいので jest.config.js
に 下記のように追記した
{
...
setupFilesAfterEnv: ['<rootDir>/test/setupJest.ts'],
testEnvironment: 'jsdom'
}
そして setupJest.tsの中身だが、Zennの記事通りだと上手くいかなかったので
Jestに立っていたIssueを読んで下記のようにした。
// Polyfill for encoding which isn't present globally in jsdom
if (typeof global.TextEncoder === 'undefined') {
global.TextEncoder = require('util').TextEncoder
}
if (typeof global.TextDecoder === 'undefined') {
global.TextDecoder = require('util').TextDecoder
}
これで完結だと思ったら 新しいエラーが出た
Unknown type, must be binary type
これがかなり困った。というのもググってもパット出てこない。
ヒットしたにはしたものの gitter.imのチャットログ的なもので、いつあった発言なのか どこまで遡れば出るかわからず 断念した。
仕方がないので エラーの at... を追ってみたところ ここの inputに Uint8Array
でなくBuffer
が入っていて バイナリ型じゃない!!って怒られているのが原因だった。試しに instanceof Uint8Array
を instanceof Buffer
にしたら次のコードまで進むが、また同様に Uint8Arrayでなく Bufferだから バイナリ型じゃない!! って怒られてしまった。 (js-ipfsでは 昔Buffer駆逐運動 があったらしく 元々Bufferで書かれていたところは全部Uint8Arrayになっている、独自に変えちゃうのは無謀な気がするのでやめた)
そこでUint8ArrayとBufferの変換について調べるとNode.jsでは Uint8ArrayはBufferのサブクラスで相互利用できる( buffer instanceof Uint8Array === true )よ的なことが書いてあった。汎用的なUint8Array以前にNode.js独自のBufferがあったという歴史的経緯からこうなったらしい。
じゃあなんで動かないのって調べたら 2017年からある、Jestのbuffer instanceof Uint8Array を破壊するっていうバグが原因だった
ここで書いてあった通りに下記をjest.config.ts
に追加して元々あるUint8Arrayをグローバルに登録し、各テスト内で 元々のUint8Arrayを見えるようにした。
globals: {
Uint8Array
},
これで今度こそ動く、と思ったら新しいエラーが出た
ReferenceError: crypto is not defined
どうやら window.crypto も jsdomでは組み込みではないよ っていう話らしい。
issueの下の方を読んで下記を setupJest.ts
に追加した
import { Crypto } from "@peculiar/webcrypto"
global.crypto = new Crypto()
これでなんとか動くはず。
動いた
完走した感想
結構苦戦したがこれで Jest(jsdom)で動いてくれた。テストをじゃんじゃん書こうと思う。ただよく見ると、testEnvironment
を jsdom
ではなくnode
に変えたらいいとも書いてあったので、nodeに変えてみたところ cryptoのエラーだけで他はいじらなくても動いてくれた。元々jestの資料に載っていたようなので、公式ドキュメントを最初に読もうね
という教訓を覚えた。