0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

DOM をスタブしたテストで、select.value が空文字になって落ちた

0
Posted at

背景

テストを書くのが後回しになりがちで、いつも実装が終わってから慌てて足しています。
今日もそうでした。
そして案の定、テストを書いた瞬間に落ちました。

しかも落ちたのはテストの中身ではなく、ファイルを読み込んだ時点です。
原因が地味に面白かったので、備忘録として残します。

前提:テストハーネスが何をしているか

私のサイトのツールは、1ツール1枚の HTML に <script> を直書きするだけの作りです。
ビルド工程を持たず、public/tools/<名前>/index.html がそのまま本番になります。

素朴で気に入っているのですが、そのままだと中の関数をテストできません。
そこで、HTML から <script> の中身を取り出して Node の vm モジュール(文字列を JavaScript として実行するための標準モジュール)で走らせるハーネスを使っています。

ブラウザの API は全部スタブです。
document.getElementById() は「何を呼んでも例外を出さない」オブジェクトを返します。

if (["value", "textContent", "innerHTML", "id", "href"].includes(prop)) return "";
if (["checked", "disabled"].includes(prop)) return false;

これは実際のハーネスの一部で、prop はアクセスされたプロパティ名です。
value を読むと空文字が返る、という点をよく見てください。ここが今日の落とし穴でした。

落ちたコード

追加していたのは、モードを選ぶ <select> を持つ計算ツールです。
モードごとに入力欄のラベルを差し替えたかったので、素直にオブジェクトを引いていました。

var LBLS = {
  mm2px: { w: '幅(mm)', h: '高さ(mm)' },
  px2mm: { w: '幅(px)', h: '高さ(px)' },
  eff:   { w: '幅(px)', h: '仕上がり幅(mm)' }
};
function labels() {
  var m = $('mode').value, L2 = LBLS[m];
  $('lw').textContent = L2.w;
  $('lh').textContent = L2.h;
}
labels();   // 読み込み直後に一度呼ぶ

$ は document.getElementById の別名、mode は <select> の id です。
ブラウザでは <select> に必ずどれかの <option> が選ばれているので、m は 'mm2px' などになります。

ところがハーネスの中では .value が空文字です。
つまり LBLS[''] が undefined になり、次の行で落ちます。

TypeError: Cannot read properties of undefined (reading 'w')
    at labels (dpi/inline.js:41:28)

labels() はスクリプト末尾で1回呼んでいるので、これはテストが1件も走る前に起きます。
node --test の出力は「テストファイルが失敗」としか言わず、書いたはずのアサーションはどこにも出てきませんでした。

直し方

1行です。

var m = $('mode').value, L2 = LBLS[m] || LBLS.mm2px;

|| で既定値に落とすだけ。
これでテストは通りました。

  • 修正前:node --test test/dpi.test.js → テスト0件・TypeError
  • 修正後:同じコマンドで5件パス

なぜこれでいいと思うか

「テストのためにコードを曲げた」ようにも見えるので、少し理由を書いておきます。

LBLS[m] が引けないのは、<select> の値が想定外だったということです。
ブラウザでもこれは起こりえます。
<option> を1つ消したのにテーブル側を直し忘れた、といった編集ミスは普通にやります。

そのとき画面が真っ白になるより、既定のモードで表示が続いたほうが親切です。
つまりこれはテスト都合の回避ではなく、未知のキーに対する防御として素直に正しい形だった、という気がします。

ルックアップテーブルを引くコードは既定値を持たせる。
今日それを、テストに教えてもらいました。

対象のツールはこれです。
https://hashitosystem.com/tools/dpi/

DOM をスタブしたテストでは、select や input の値は空文字で返ってくる。だからルックアップテーブルを引く行は必ず既定値でフォールバックさせておく。これを守るだけで、テストが1件も走らないまま落ちる事故は防げます。


本記事はAI補助で執筆した、個人開発の紹介記事です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?