💭

JotaiでAtomの挙動をテストする方法

2023/08/24に公開

Jotaiを用いてReact開発している際、作ったAtomの挙動をテストしたいことがあるかと思います。調べてみると思いの外参考になる情報が無かったので、簡単なテスト方法を紹介します。

ロジック部分を別関数に切り分け、その関数単体でテストを行う方針も考えられますので、そちらを検討してみるのも良いかもしれません。

テスト方法

storegetメソッドやsetメソッドを用いてテストを用いるのが良いです。useAtomuseAtomValueuseSetValueなどはHookなので基本的にはReactコンポーネント内で使用します。そのため、Atomがちゃんと値を計算してくれるかなどをJestでお手軽にテストするのには向きません。

JotaiにおいてAtomは大きく分けて2つあります。primitive atomderived atomです。derived atomはprimitive atomを用いて計算されるatomになります(Recoilのselectorに近いかも)。derived atomは以下の3つに区分出来ます。

  • Read-only Atom
  • Write-only Atom
  • Read-Write Atom

テストの観点から言うとRead-Write Atomは先2つと同じなのでこの記事では割愛します。

Read-only Atomのテスト

Read-only AtomはAtomの値を読み取るAtomです。例えば以下のdoubledNumberAtomはread-only atomです。

const numberAtom = atom(0); // primitive atom

const doubledNumberAtom = atom((get) => get(numberAtom) * 2);

このAtomはstoreを用いて以下のようにテストすることが出来ます。

test("should add 1", () => {
  const store = createStore();
  store.set(numberAtom, 1); // numberAtomの初期値を設定
  const newNumber = store.get(doubledNumberAtom); // doubledNumberAtomの値を読み出す

  expect(newNumber).toBe(2);
})

Write-only Atomのテスト

Write-only Atomは文字通りAtomの値を更新するだけのAtomです。もちろんreadすることは出来ません。例えば以下のようなWrite-only Atomを考えます。

const numberAtom = atom(0); // primitive atom

const addNumberAtom = atom(
  null,
  (get, set, num: number) => set(numberAtom, get(countAtom) + num)
);

addNumberAtomnumberAtomの更新を行うだけのAtomです。read関数を持たずcountAtomなどの値を読むわけではありません。例えば以下のようにコンポーネント内で用います。

const Component = () => {
  const setAddNumberAtom = useSetAtom(addNumberAtom);

  return (
    <button onClick={() => setAddNumberAtom(1)}>increment</button>
  )
}

こちらも以下のようにstoreを用いてテストすることが出来ます。

test("should add 1", () => {
  const store = createStore();
  store.set(numberAtom, 1); // numberAtomの初期値を設定
  store.set(addNumberAtom, 1); // addNumberAtomとその引数を渡す
  const newNumber = store.get(numberAtom); // 更新されたnumberAtomの値を読み出す

  expect(newNumber).toBe(2);
})

引数を受け取らないWrite-only Atomの場合はsetで引数を渡さなくて大丈夫です。

const incrementNumberAtom = atom(
  null,
  (get, set) => set(numberAtom, get(countAtom) + 1)
);

test("should add 1", () => {
  const store = createStore();
  store.set(numberAtom, 1);
  store.set(addNumberAtom); // 引数無しでOK
  const newNumber = store.get(numberAtom);

  expect(newNumber).toBe(2);
})
GitHubで編集を提案

Discussion