第8回 | 小さな不具合修正をCLIで最後まで進める | 探索、編集、テスト、Git記録を通して見る|UNIX Cafe

* 当サイトでは、コンテンツの一部に広告を掲載しています。

System Note $ cat /proc/ai-disclosure

本記事の構成および論理分析にはAI(人工知能)を使用しています。情報の正確性は、システム管理者(UNIXユーザー)による手動検証済みです。

第8回 | 小さな不具合修正をCLIで最後まで進める | 探索、編集、テスト、Git記録を通して見る|UNIX Cafe

CLIで開発する | 第8回

ここまでのシリーズでは、CLIでファイルを見て、アプリを動かし、ログを読み、エラーの場所を探し、vi で小さく直し、テストで確かめ、Gitで変更を記録する流れを少しずつ見てきました。

今回は最終回として、それらを1本の開発作業としてつなげます。題材は、注文の合計金額が数量を反映していない不具合です。

第7回では、変更をGitに記録する流れを詳しく見ました。今回はそのGit操作も含めて、不具合修正の作業全体を最初から最後まで通してみます。

大切なのは、いきなり直し始めないことです。まず不具合を再現し、関係しそうな場所を探し、原因を小さく確認してから、1行だけ直します。そのあと、テストとアプリ実行で確かめ、差分を読んでGitに記録します。

この記事で学べること

  • 不具合を修正前に再現する流れ
  • grep とテストで調査範囲を絞る方法
  • vi で計算処理を小さく修正する方法
  • テスト、アプリ実行、差分確認、Git記録までを通す流れ
目次

練習を始める前の準備

第7回で使った unix_cafe_order ディレクトリで作業を続けます。別の場所にいる場合は、作業用ディレクトリへ移動してください。

pwd
  • pwd は、現在いる場所を表示するコマンドです。
  • unix_cafe_order の中にいることを確認してから進めます。

作業を始める前に、git status で現在の状態を確認します。

git status

次のように表示されれば、作業ディレクトリはきれいな状態です。

On branch main
nothing to commit, working tree clean

前回の修正作業で、サンプルコードは正しく動く状態になっています。今回も、修正前の不具合を自分で作ってから学習を始めます。

これは練習のための準備作業です。実際の開発で、正しく動いているコードを理由もなく壊すことはありません。修正前と修正後の違いを git diff で見るための開始状態を作っています。

練習用ファイルの準備

visrc/calculator.py を開きます。

vi src/calculator.py

次の行から、* int(order["quantity"]) を削除します。

total += int(order["price"]) * int(order["quantity"])

練習用の開始状態は、次の形です。

def calculate_total(orders):
    total = 0
    for order in orders:
        total += int(order["price"])
    return total

保存したら、不具合を作ったことで Git からどう見えるかを確認します。

git status

実行すると、次のような内容が表示されます。

On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   src/calculator.py
	
	no changes added to commit

この練習用の不具合状態をGitに記録します。こうしておくと、このあと修正した差分を git diff で確認できます。

git add src/calculator.py
git commit -m "Prepare quantity bug practice"

ここまでが準備です。ここから本編として、不具合を再現し、調べて、直して、記録します。

現在地のファイル構成を確認する

まず、今いるディレクトリのファイル構成を確認します。

ls

次に、ファイル構成を見ます。

data      logs      README.md      src       tests

ここでは、アプリ本体が入っている src ディレクトリと、テストが入っている tests ディレクトリがあることを確認します。

アプリを動かして不具合を再現する

修正する前に、まずアプリを実行して現在の状態を確認します。

python3 src/app.py

この状態では、合計金額が次のように表示されます。

Order total: 1350 yen

注文には coffee が2つ含まれているため、この金額は少なそうです。期待する合計は、数量を反映した 1800 yen です。

ここで大事なのは、「なんとなくおかしい」ではなく、「実際にこう表示された」と確認することです。修正前の状態を見ておくと、あとで直ったかどうかを比べられます。

grep で関係する場所を探す

合計金額に関係しそうな場所を探します。今回は、total という単語を手がかりにします。

grep -Rni --exclude-dir=__pycache__ total src tests
  • -R は、ディレクトリの中を再帰的に探す指定です。
  • -n は、見つかった行番号を表示する指定です。
  • -i は、大文字と小文字を区別せずに探す指定です。
  • --exclude-dir=__pycache__ は、Pythonが作るキャッシュディレクトリを検索結果から外す指定です。

実行すると、次のように表示されます。

src/message.py:1:def format_total(total):
src/message.py:2:    return f"Order total: {total} yen"
src/calculator.py:1:def calculate_total(orders):
src/calculator.py:2:    total = 0
src/calculator.py:4:        total += int(order["price"]) 
src/calculator.py:5:    return total
src/app.py:6:from calculator import calculate_total
src/app.py:7:from message import format_total
src/app.py:32:    total = calculate_total(orders)
src/app.py:33:    logging.info("calculated total: %s", total)
src/app.py:34:    print(format_total(total))
tests/test_calculator.py:8:from calculator import calculate_total
tests/test_calculator.py:10:class CalculateTotalTest(unittest.TestCase):
tests/test_calculator.py:13:        self.assertEqual(calculate_total(orders), 900)

この中で、主に次の2つが関係していそうです。

  • src/calculator.py: 合計金額を計算する処理
  • tests/test_calculator.py: 合計金額を確認するテスト

いきなりすべてのファイルを読むのではなく、検索で調査範囲を小さくします。

テストで失敗内容を確認する

次に、テストを実行します。アプリの表示だけでなく、計算処理だけを切り出して確認します。

python3 -m unittest discover -s tests -v

失敗すると、期待した値と実際の値が表示されます。

AssertionError: 450 != 900

これは、テストが期待している 900 に対して、実際の計算結果が 450 だったという意味です。価格450円の商品を数量2で注文しているのに、数量が計算に入っていない可能性が高くなりました。

vi で計算処理を修正する

原因の見当がついたので、計算処理を確認します。

vi src/calculator.py

修正前の計算処理は、価格だけを足しています。

total += int(order["price"])

この行では、quantity が使われていません。数量を反映するために、価格と数量を掛ける形にします。

total += int(order["price"]) * int(order["quantity"])

変更できたら、Esc:wqEnter の順に操作して保存します。

今回は1行だけ直します。小さく直すと、あとで差分を読んだときに「何を変えたのか」が分かりやすくなります。

テストをもう一度実行する

修正したら、同じテストをもう一度実行します。

python3 -m unittest discover -s tests -v

今度は、次のように成功します。

test_quantity_is_included (test_calculator.CalculateTotalTest.test_quantity_is_included) ... ok

----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

テストが通ったことで、少なくとも合計金額を計算する関数は期待どおりに動いていると確認できました。

アプリでも確認する

テストだけでなく、アプリももう一度実行します。

python3 src/app.py

合計金額が次のように表示されれば、画面上の動きとしても直っています。

Order total: 1800 yen

テストで確認し、アプリでも確認する。この2つを組み合わせると、修正の手応えがはっきりします。

git statusgit diff で変更を見る

動作確認ができたら、Gitで変更状態を確認します。

git status

src/calculator.py が変更されていることを確認します。

次に、実際にどこを変えたのかを読みます。

git diff

差分では、価格だけを足していた行が、価格と数量を掛ける行に変わっていることが分かります。

-        total += int(order["price"])
+        total += int(order["price"]) * int(order["quantity"])

ここまで読めれば、「注文合計に数量を反映するため、計算式に quantity を追加した」と説明できます。

git add で記録対象を選ぶ

差分を確認できたら、今回のコミットに含める変更を選びます。

git add src/calculator.py

git add を実行しても、成功した場合は何も表示されません。記録対象に入ったかどうかは、次のコマンドで確認できます。

git diff --staged

git diff --staged は、次のコミットに入る予定の差分を見るコマンドです。コミット前にここを読むことで、余計な変更を混ぜていないか確認できます。

表示される差分は、先ほど確認した内容と同じです。

-        total += int(order["price"]) 
+        total += int(order["price"]) * int(order["quantity"]) 
  • git diff は、まだ git add していない変更を見ます。
  • git diff --staged は、すでに git add して、次のコミットに入る予定の変更を見ます。

git commit で変更を記録する

テスト、アプリ実行、差分確認ができたので、変更をGitに記録します。

git commit -m "Fix quantity handling in order total"

このコミットメッセージは、「注文合計で数量の扱いを修正した」という意味です。あとから履歴を見たときに、何を直した変更なのかが分かります。

git log --oneline で履歴を確認する

最後に、コミットが履歴に残ったことを確認します。

git log --oneline

先ほどのコミットメッセージが一番上に表示されれば、今回の修正はGitの履歴に記録されています。

今回の開発の流れを整理する

今回の作業は、次の順番で進めました。

  1. pwdls で現在地と構成を確認する
  2. python3 src/app.py で不具合を再現する
  3. grep で関係するファイルを探す
  4. unittest で失敗内容を確認する
  5. vi で計算処理を1行だけ修正する
  6. テストとアプリ実行で修正結果を確認する
  7. git diff で変更内容を読む
  8. git addgit diff --staged で記録予定を確認する
  9. git commit で履歴に残す

CLIで開発する流れは、特別な操作を一度に覚えることではありません。確認する。探す。直す。動かす。テストする。差分を見る。記録する。この小さな流れを、落ち着いて繰り返すことです。

手を動かすミニ練習

最後に、今回の流れを自分の手で確かめてみましょう。

  1. python3 src/app.py で修正前と修正後の合計金額を比べる
  2. grep -Rni --exclude-dir=__pycache__ total src tests の結果から、処理とテストの場所を説明する
  3. git diff を見て、変更内容を自分の言葉で説明する
  4. git log --oneline で作成したコミットを確認する

シリーズのまとめ

「CLIで開発する」シリーズでは、コマンドを単体で覚えるだけでなく、開発作業の中でどう使うかを見てきました。

  • ファイル構成を見る
  • アプリを動かす
  • ログやエラーを見る
  • 検索して場所を絞る
  • vi で小さく直す
  • テストで確かめる
  • Gitで変更を記録する

これらは別々の知識ではなく、1つの開発作業の中でつながっています。小さく確認しながら進めることが、CLIで開発するときの基本です。

今回で「CLIで開発する」シリーズは一区切りです。ここまでの流れを何度か繰り返すと、ターミナル上での開発作業が少しずつ自然になります。

もう一度、はじめからゆっくりと

ここまで読み進めてきた今、もう一度第1回に戻ってみてください。

最初は、pwdls で現在地を確認するだけでも、少し手探りに感じたかもしれません。けれど、アプリを動かし、エラーを読み、テストで確かめ、Gitに記録するところまで進んだ今なら、同じコマンドも少し違って見えるはずです。

CLIでの開発は、一度読んだだけで身につくものではありません。現在地を確認し、ファイルを見て、少し直し、結果を確かめる。その流れを何度か行き来しながら、少しずつ手に馴染んでいく道具です。

第1回に戻ることは、後退ではありません。むしろ、これまで見てきた流れを、自分の中でつなぎ直すための大切な一歩です。

👉 もう一度、第1回から読み直してみましょう。

さらに学びたいあなたへ

用途ごとに選ぶ Linux のおすすめ本

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

のいのアバター のい UNIX Cafe マスター

Macintosh Color Classicから始まった旅は、長いWindows時代を経て、Windows10のサポート終了をきっかけにUNIXの世界へ戻ってきました。UNIX Cafeでは、UNIX・Linux・そしてMacな世界を、むずかしい言葉を使わず、物語のように書いています。プログラミングは、アイデアをコンピューターに伝えるための言葉です。簡単な単語と文法を覚えれば、誰でもコマンドを使えます。ぜひ一度、やさしいプログラミングの世界をのぞいてみてください。

Created by UNIX Cafe

目次