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?

自分が実装していない機能の総合テストで、コードから前提条件を逆算して原因を切り分けた話

0
Posted at

はじめに

総合テストで、自分が実装していない複数の機能を担当する機会がありました。

そこで困ったのが、用意されていたテストデータをそのまま使っても、確認したい処理まで到達できないケースが多かったことです。

実行するとエラーになる。

しかしコードを確認すると、今回テストしたい処理そのものではなく、その前段階で処理が終了している。

さらに調べると、テストデータの前提条件が合っていなかったり、ファイルにBOMなどの意図しないデータが含まれていたりと、原因も一つではありませんでした。

しかも、自分が実装した機能ではないため、最初から処理全体を把握していたわけでもありません。

そこで、

「エラーを一つずつ直す」

のではなく、

「今回確認したい処理はどこで、そこへ到達するには何が必要なのか」

をコードから逆算して調べることにしました。

結果的にこの経験は、テストというより、既存システムをコード・ログ・データから読み解いていく経験になりました。


最初にやったのは「テスト対象」を明確にすること

例えば、次のような処理があるとします。

確認したいのはDです。

ところが実際にテストを実行すると、Cで例外が発生することがありました。

この状態では、

「テスト結果はNG」

とはまだ判断できません。

そもそもDが実行されていないからです。

そこで、エラーが発生したときにまず、

「今回確認したい処理まで到達しているか」

を見るようにしました。


ログだけでは分からなければコードまで追う

エラーが発生した場合は、ログから発生箇所を確認します。

ただ、ログだけでは、

「なぜこのデータでその処理に入ったのか」

まで分からない場合があります。

その場合は実際のコードを追いました。

ここで意識していたのは、

「エラーを消すこと」

ではなく、

「どの処理まで正常で、どこから想定と違っているのか」

を明確にすることでした。


知らない機能だからこそ、コードから入力条件を逆算した

自分が実装していない機能では、

「どんなデータならこの処理を通るのか」

が最初から分かるとは限りません。

資料を確認しても判断できない場合は、実装から逆算しました。

例えば次のような条件があったとします。

if (status.equals("01") && type.equals("A")) {
    executeTargetProcess();
}

今回確認したい処理が executeTargetProcess() なら、

status = "01"
type   = "A"

を満たす必要があります。

さらに、この値がDBから取得されているのであれば、

というように、値の出どころまで遡ります。

こうして、

「正しそうなデータを試す」

のではなく、

「コード上、この処理に到達できるデータを作る」

という考え方に変えていきました。


テストデータ自体にも問題があった

もう一つ難しかったのが、

「渡されたテストデータが必ずしも正しいとは限らない」

ということでした。

例えば、

  • 必須項目の不足
  • 値の組み合わせの不整合
  • 処理の前提条件を満たしていない
  • ファイル形式の問題
  • BOMなど意図しないデータの混入

などです。

見た目では正しそうでも、プログラムから読み込むと想定した値にならないことがあります。

そのため、

「用意されたデータだから正しい」

ではなく、

「実装が期待している入力条件を満たしているか」

を確認するようにしました。

ここは、テストを進めるうえで自分の考え方が大きく変わった部分でした。


「バグ」と「テスト対象に到達するための問題」を分ける

実行時にエラーが発生すると、最初はすべて同じ「エラー」に見えます。

しかし実際には、

と分けて考える必要があります。

特に大事だったのが、

「今回確認したい処理と、このエラーには本当に関係があるのか?」

という視点でした。

対象処理より前で落ちているのであれば、まず対象処理まで到達できる状態を作る。

対象処理で初めて問題が発生したのであれば、そこで本来のテスト結果として調査する。

この切り分けを行うことで、エラーが発生するたびに闇雲に修正するのではなく、調査する範囲を絞れるようになりました。


生成AIは「答え」ではなく「調査の補助」に使った

処理を理解するために生成AIも利用しました。

特に、自分が実装していないコードを読む際に、

  • このコードは何をしているのか
  • この条件分岐にはどんな意味がありそうか
  • この例外が発生する原因として何が考えられるか
  • 次にどこを確認すべきか

といった仮説整理に使いました。

ただし、生成AIはこのシステム固有の仕様を知りません。

そのため、回答をそのまま正解として扱うのではなく、

という使い方をしていました。

AIで調査速度を上げつつ、最終的な判断材料は実際のコードと実行結果にする。

生成AIを開発で使ううえで、この考え方は今も大切にしています。


今振り返ると、もっと仕組み化できた

この対応では、かなりの回数、

実行
↓
エラー
↓
ログ確認
↓
コード確認
↓
データ修正
↓
再実行

を繰り返しました。

必要な調査ではありましたが、今同じ状況になったら、もう少し違う進め方をすると思います。

まず、テスト対象までの条件を先に整理します。

さらに同じデータ不備が繰り返されるのであれば、

  • 前提条件のチェックリスト化
  • SQLによる事前チェック
  • データ検証処理のスクリプト化
  • 正常系の基準データを作り、そこから差分だけ変更する

といった方法も検討できます。

当時は「一つずつ原因を特定してテストを進める」ことで精一杯でした。

ただ、そこまで経験したからこそ、

「同じ調査を繰り返しているなら、人が頑張るのではなく仕組みにできないか」

という視点を持つようになりました。


この経験から身についた「知らないコードの読み方」

この経験で一番変わったのは、

自分が実装していないコードに対する向き合い方でした。

以前なら、

「この機能について詳しく知らない」

こと自体を大きな壁として感じていました。

しかし、すべてを理解してから調査を始める必要はありませんでした。

まず目的を決める。

そこから必要なコードを読む。

値の出どころを追う。

ログと実行結果で仮説を検証する。

必要になった範囲だけ、さらに理解を広げていく。

この進め方を覚えたことで、知らない機能でも「全部分からないから手が出せない」ではなく、「まずどこから調べればいいか」を考えられるようになりました。


まとめ

総合テストを担当したこと自体は、最初はそれほど特別な経験だとは思っていませんでした。

ただ振り返ってみると、実際にやっていたのは単純なテスト実施だけではありませんでした。

未知の機能に対して、

というサイクルを繰り返していました。

この経験から学んだのは、

「知らない機能だから分からない」で止まる必要はないということです。

コード、ログ、DB、入力データ、実行結果。

手元にある事実を一つずつつなげていけば、少しずつ処理の全体像は見えてきます。

そして今なら、原因を一件ずつ解決するだけでなく、前提条件の整理やデータチェックの自動化など、同じ問題を繰り返さない方法まで考えたいと思っています。

既存システムでは、資料もデータもコードも、最初からすべて理解できる状態で仕事が始まるとは限りません。

そんな状況でも、

「何が分からないのか」
「次にどこを確認すればいいのか」
「確認した事実から何が言えるのか」

を一つずつ整理して前に進める。

この経験は、その後の障害調査や既存コードを読むときの、自分の基本的な進め方にもつながっています。

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?