このツールについて
複数の個人開発プロジェクトの進捗を検知して、SNSや技術記事投稿サービスへ投稿するツールです。普段は他のプロジェクトの進捗を投稿するために使っていますが、今回はこのツール自身の不具合が見つかったので、初めてこのツール自身を題材にした記事を書きます。
見つかった問題
別の新しいプロジェクトを追加する作業をしていた際、副産物として、以前追加していたはずの1つのプロジェクトが、実は投稿対象として認識されていないことに気づきました。ただ、その時点では他の作業を優先し、対応は後回しにしていました。今回、あらためてこの問題に向き合い、実際に直しました。
調べてみると、「どのプロジェクトを投稿対象にするか」を書く場所が、1箇所ではなくコード内の6ファイルに分かれて存在していました。新しいプロジェクトを追加するときは、この6箇所すべてに手作業で追記する必要があります。問題のプロジェクトは、そのうち2箇所には反映されていましたが、残り6ファイルには最初から書き忘れられたままになっていました。
もう一つ、別の問題も見つかりました。すでに投稿対象から外すと決めていたはずの、あるプロジェクトが、曜日ごとの投稿スケジュールの設定にはまだ残ったままになっていたのです。
なぜ追加した直後に気づけなかったのか
このツールには、投稿状況を確認するコマンドがあります。ここで実は、性質の異なる2種類の集計が混在していました。
# ① 過去に投稿した記録があるプロジェクトを一覧表示する
# state.json に記録があれば、それをそのまま辿るだけ
for pj_id in sorted(state.keys(), ...):
...
# ② 今日の投稿準備が何件揃っているかを集計する
# こちらは6ファイルにある一覧リストだけをループする
for pj_id in _PJ_LIST:
latest, data = _read_latest_draft(pj_id)
...
①は、実際に投稿した記録(state.json)をそのまま辿るので、新しいプロジェクトも一度投稿を始めれば普通に表示されます。問題は②のほうで、こちらは6ファイルにある一覧リストをループするだけの実装でした。このリストに入っていないプロジェクトは、そもそもループの対象にすらなりません。
つまり、新しいプロジェクトは①の一覧には普通に表示されていて、投稿自体も問題なくできていました。それにもかかわらず、②の「今日の投稿準備が何件揃っているか」という運用チェックからだけ、静かに対象外になり続けていたのです。一覧には見えているのに、チェックリストからは漏れている、という状態だったため、パッと見ただけでは異常に気づけませんでした。
なぜテストで検知できなかったのか
このツールには、プロジェクトの一覧を対象にした自動テストも444件存在しています。それでも、この抜け漏れはテストで検知されませんでした。原因は、実装側のリストとテスト側の期待値が、同じ構造で作られていたことにあります。
# 実装側(6ファイルそれぞれにこの形のリストがある)
_PJ_LIST = [...] # プロジェクトIDを列挙
# テスト側(テストコードにも同じ値を独立にハードコード)
_ALL_PJS = [...] # 実装側と同じ並びを、テスト側で別に書いている
実装側の_PJ_LISTとテスト側の_ALL_PJSは、値としては同じプロジェクトIDの並びですが、コード上はまったく別の場所に書かれた、独立したハードコードです。テストが検証しているのは「実装のリストと、テストが期待するリストが一致しているか」であって、「そのリストの中身が本当に正しいか」ではありません。
新しいプロジェクトを追加したとき、実装側の6ファイルへの反映が漏れていたのと同時に、テスト側の_ALL_PJSも更新されていませんでした。両方が同じタイミングで同じ抜け漏れを持っていたため、実装とテストの値は常に一致し続け、テストはずっとパスしていました。テストが見ていたのは「実装が正しいプロジェクト一覧を持っているか」ではなく「実装とテストの値が食い違っていないか」だけだった、ということになります。
対応と確認
投稿対象のプロジェクト名を書く6つのファイルと、曜日ごとの投稿スケジュールの設定、そしてテスト側のフィクスチャを、それぞれ直しました。状態を確認するコマンドを実行したところ、新しく追加したプロジェクトもきちんと表示されるようになっていることを確認できました。テスト444件も全て問題なく通っています。
まとめ
他の個人開発プロジェクトの進捗を検知するために作ったこのツール自身にも、同じ種類の見落としが起きていました。原因を辿ると、「1箇所を直せば済む」設計ではなく「複数箇所に手作業で反映する」設計だったことに加えて、テスト自体もその複製構造をそのまま引き継いでいたため、テストが本来の役割(実装の正しさを検証すること)を果たせていなかったことが根本の原因だったと思います。次に新しいプロジェクトを追加するときのために、管理場所を1箇所へまとめることも今後の課題として考えたいです。気になる点・ご質問があればコメントでお知らせください。