JestでOrbitDBを使うテストをなんとかしてやった

# 背景
卒業研究でWebフロントエンドで動作する分散型の図書館WebアプリをNuxt.js+TypeScriptで開発しています。このWebアプリではデータベースとしてIPFS上にデータを保管するOrbitDBを採用しており、ブラウザ上で直接P2Pネットワークに参加して接続を確立してDBをやり取りします。OrbitDB/IPFSを利用することで、複数の図書館で共通のデータベースを扱うことができるため、蔵書や貸し出し状態を容易にやり取りでき、いろんな点で面白い/便利なんじゃないかと思いはじめた研究です。

https://github.com/Dosugamea/distributed-library

これを作るためには複数のデータベースを扱う必要が出てくるため、大量のDAO、model、Typeを定義しました。これらすべてをブラウザ上でいちいち手動で確認はしていられません。 なので、使うのは初めてですが、Jestを使って テストをしたくなりました。

## テストしたい内容
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254984/3765d499-d91e-7bd8-8063-d276bb1f2015.png" width="500px">

### beforeAllの内容
全体テスト前にIPFSインスタンスを作成、IPFSインスタンスを渡してOrbitDBインスタンスを作成
BibliographyDaoを作成、BibliographyDaoを初期化(コンストラクタ無いでasyncが使えなかったのでやむおえず分離)

### testの内容
蔵書情報モデルを作成
蔵書情報をOrbitDBに追加
追加結果などログ表示


## 実際テストしてみた

## ReferenceError: TextDecoder is not defined.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254984/4099e4b5-b39c-f0a0-eb28-16a0c79cf856.png)
と言われて動かなかった。検索したところ既にissueと記事があった。jsdomじゃ組み込みのTextDecoderが無いよっていう話らしい。

https://github.com/ipfs/js-ipfs/issues/3620

https://zenn.dev/ocknamo/scraps/f0de16520e7117

どうやら `setupJest.ts` とやらを作って jest起動時に細工をすればいいらしいので `jest.config.js`に 下記のように追記した

```
{
    ...
    setupFilesAfterEnv: ['<rootDir>/test/setupJest.ts'],
    testEnvironment: 'jsdom'
}
```
そして setupJest.tsの中身だが、Zennの記事通りだと上手くいかなかったので
Jestに立っていたIssueを読んで下記のようにした。

https://github.com/facebook/jest/issues/9983#issuecomment-626489847

```
// 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
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254984/fc7e3373-7fb1-b45f-7e41-541496557531.png)

これがかなり困った。というのもググってもパット出てこない。
ヒットしたにはしたものの gitter.imのチャットログ的なもので、いつあった発言なのか どこまで遡れば出るかわからず 断念した。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254984/3b6a83e1-8604-cc76-cfe5-d4b7b1db7564.png)

仕方がないので エラーの at... を追ってみたところ ここの inputに `Uint8Array` でなく`Buffer` が入っていて バイナリ型じゃない!!って怒られているのが原因だった。試しに `instanceof Uint8Array` を `instanceof Buffer` にしたら次のコードまで進むが、また同様に Uint8Arrayでなく Bufferだから バイナリ型じゃない!! って怒られてしまった。 (js-ipfsでは 昔[Buffer駆逐運動](https://github.com/ipfs/js-ipfs/issues/3220
) があったらしく 元々Bufferで書かれていたところは全部Uint8Arrayになっている、独自に変えちゃうのは無謀な気がするのでやめた)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254984/2dcde1a0-9583-b6ce-46e1-6358056d5595.png)

そこでUint8ArrayとBufferの変換について調べるとNode.jsでは Uint8ArrayはBufferのサブクラスで相互利用できる( buffer instanceof Uint8Array === true )よ的なことが書いてあった。汎用的なUint8Array以前にNode.js独自のBufferがあったという歴史的経緯からこうなったらしい。

https://stackoverflow.com/questions/54814215/node-js-buffer-vs-uint8array

じゃあなんで動かないのって調べたら 2017年からある、Jestのbuffer instanceof Uint8Array を破壊するっていうバグが原因だった

https://github.com/facebook/jest/issues/4422

ここで書いてあった通りに下記を`jest.config.ts`に追加して元々あるUint8Arrayをグローバルに登録し、各テスト内で 元々のUint8Arrayを見えるようにした。

```
  globals: {
    Uint8Array
  },
```

これで今度こそ動く、と思ったら新しいエラーが出た

## ReferenceError: crypto is not defined

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254984/f252d4e4-77f5-79f0-fbed-fefbf63540a8.png)

https://github.com/jsdom/jsdom/issues/1612

どうやら window.crypto も jsdomでは組み込みではないよ っていう話らしい。
issueの下の方を読んで下記を `setupJest.ts` に追加した

```
import { Crypto } from "@peculiar/webcrypto"

global.crypto = new Crypto()
```

これでなんとか動くはず。


## 動いた
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254984/05522ae5-9cd7-502b-f469-f44971b621aa.png)

## 完走した感想
結構苦戦したがこれで Jest(jsdom)で動いてくれた。テストをじゃんじゃん書こうと思う。ただよく見ると、`testEnvironment`を `jsdom`ではなく`node`に変えたらいいとも書いてあったので、nodeに変えてみたところ cryptoのエラーだけで他はいじらなくても動いてくれた。元々jestの資料に載っていたようなので、`公式ドキュメントを最初に読もうね`という教訓を覚えた。

## その他補足資料

https://jestjs.io/ja/docs/configuration#globals-object

https://qiita.com/yosuke_furukawa/items/8c14b7b4461cc109bc54