- 公開日
AIが示したAPIが古そうなときは、まず利用する版で「非推奨なのか、既に廃止されているのか」を確認します。公式の移行情報から置換先を選び、引数だけでなく、処理対象や失敗時の挙動が保たれるかを試験します。名前の似た関数へ差し替えてエラーが消えただけでは、移行の完了とは判断できません。
とくにテストを読み込むAPIの移行では、テストが実行されなくなったために成功表示が出るという見落としに注意が必要です。以下では架空のテストコードを使い、Python 3.12系から3.13系へ移す際の確認方法を説明します。コード、差分、確認例はすべて未実行で、示す結果は期待値です。
非推奨と廃止を対象版で区別する
非推奨は、提供者がそのAPIの利用を勧めず、代替への移行を求めている状態です。対象版では呼び出せることもありますが、今後も使い続けられるという保証にはなりません。一方、廃止・削除は、その版ではAPIが提供されなくなった状態です。資料で使われる用語と対象範囲を確かめ、警告の有無だけで分類しないようにします。
Pythonの3.13変更情報のunittest削除項目には、unittest.makeSuiteなどが3.11で非推奨になり、3.13で削除されたことと、TestLoaderのメソッドを使う移行先が記されています。この例は、削除が起きた3.13という歴史的な境界を説明するものです。3.13を現在の最新版として紹介しているわけではありません。
| 確認する環境 | makeSuiteの扱い | 実装担当者の判断 |
|---|---|---|
| 移行前のPython 3.12系 | 旧APIは存在し、非推奨 | 旧実装の設定と挙動を把握して移行準備をする |
| 移行先のPython 3.13系 | 公式に削除済み | 旧呼び出しを残して採用しない |
| 版が不明な実行環境 | その環境での状態は未確認 | 環境を特定してから資料と照合する |
「非推奨なら直ちに使えない」「動いたなら非推奨ではない」という理解はいずれも不正確です。また、廃止とセキュリティ上の欠陥は別の論点です。廃止されたという情報だけから、そのAPIを使った全コードに脆弱性があるとは断定できません。ただし、移行先で存在しない呼び出しを残せば、その経路は期待どおりに動かせません。
旧コードを3.12系で実行した記録は、3.13系での互換性を証明しません。ローカルの開発環境、テスト環境、配布先が異なる版を使う場合は、それぞれの対象版を並べます。必要な対応範囲を決めるのはプロジェクト側であり、AIが最も新しそうな版を選べばよいわけではありません。
公式資料から置換の範囲を決める
調査では、旧APIの完全な名前、利用元のパッケージ、呼び出し箇所、対象版を揃えます。「テスト用の古い関数」という検索だけでは、似た目的の別APIや別言語の情報が混ざります。標準ライブラリでも、版を切り替えた文書で同じ項目が見つかるかを確認します。
この例では、3.13の変更情報で削除と移行先を確認し、3.13のTestLoader仕様で呼び方を確かめます。さらに、旧APIの引数がどの設定へ渡されていたかをCPython 3.12の公式ソースのmakeSuiteと_makeLoaderで照合します。文書で確認できる結論と、ソースを追加で読んだ理由を分けると、推測の差し替えを避けられます。
確認メモには、資料名とURLだけでなく、「3.13でmakeSuiteが削除」「TestCaseクラスにはloadTestsFromTestCase」「prefixとsortUsingはローダー設定へ引き継ぐ」のように、変更判断と該当箇所を対応付けます。提供者の移行先が一つでも、利用側の設定によって修正箇所は増えるためです。
古いソース例を調べる目的は、旧コードの意味を復元することです。それを対象版の新しい使い方の根拠として、そのまま採用するのは避けます。また、外部ライブラリが旧APIを内部で使っている場合には、自分の一行だけを直しても解決しません。依存先の対応版と変更情報を確認し、未対応なら更新方法や移行時期を別に判断します。
テストの選択条件を残して置き換える
架空の配送システムで、荷物の個数からラベルを二枚ずつ用意する関数をテストするとします。対象はPython 3.13系の標準unittestで、外部ライブラリ、ファイル、ネットワークを使わない説明用の例です。旧コードでは、通常のtestという接頭辞ではなく、check_で始まるメソッドを試験として選んでいたとします。
#未実行の説明例。次のクラスは移行前後で変更しない。
import unittest
def label_count(count):
if type(count) is not int:
raise TypeError("count must be an integer")
if count < 0:
raise ValueError("count must not be negative")
return count * 2
class LabelTests(unittest.TestCase):
def check_normal(self):
self.assertEqual(label_count(3), 6)
def check_negative(self):
with self.assertRaises(ValueError):
label_count(-1)
def helper_note(self):
return "not a test"
def compare_names(left, right):
return (left > right) - (left < right)
この例の入力契約は、真偽値を含まない整数で、負数は拒否するというものです。型検証全体の網羅性を示す例ではなく、移行前後で同じ正常テストと例外テストを選べるかを見るために二件へ絞っています。二件のテストが通っても、この関数のすべての仕様を検証したことにはなりません。
旧呼び出しからの差分は次のとおりです。削除行は3.12系の旧コードを表し、追加行が3.13系の移行候補です。差分自体をPythonとして実行するものではありません。
-suite = unittest.makeSuite(
- LabelTests, prefix="check_", sortUsing=compare_names
-)
+loader = unittest.TestLoader()
+loader.testMethodPrefix = "check_"
+loader.sortTestMethodsUsing = compare_names
+suite = loader.loadTestsFromTestCase(LabelTests)
loadTestsFromTestCaseには、テストを実装したTestCase派生クラスを渡します。ここではクラスのインスタンスや、クラス名の文字列へ引数を変えていません。別の読込メソッドが受け取る形式を、名前の印象だけで混ぜないようにします。
移行で残すのは関数の目的だけではありません。旧prefixに対応するtestMethodPrefixと、旧sortUsingに対応するsortTestMethodsUsingを設定しています。この例では名前の辞書順に並べる比較関数を、旧コードと移行後で共用します。旧コードで独自のsuiteClassを指定していた場合は、その役割も確認が必要です。この差分は、独自スイートを使わない今回の前提に限ります。
TestLoaderはテストを収集し、実行するまとまりであるスイートを作ります。スイートが返ったことは、テストが実行された証拠ではありません。選択された名前・件数を確認してから、ランナーによる実行と結果の照合へ進むように分けます。
正常に通ることと誤りを検出できることを確かめる
最初に照合する期待値は、選択名がcheck_negative、check_normalの二件で、その順番になっていることです。helper_noteは含みません。全件が一件ずつ選ばれたことを確認すれば、件数が偶然同じでも別の試験へ置き換わっていたという違いを見つけられます。
次は確認方法を示す未実行コードです。先ほどの関数・クラス・比較関数の定義に続けて使う計画で、実行に使うスイートは新しく生成します。
#未実行。読込確認と実行計画を分ける。
loader = unittest.TestLoader()
loader.testMethodPrefix = "check_"
loader.sortTestMethodsUsing = compare_names
expected_names = ["check_negative", "check_normal"]
actual_names = loader.getTestCaseNames(LabelTests)
if actual_names != expected_names:
raise RuntimeError("unexpected test selection or order")
suite = loader.loadTestsFromTestCase(LabelTests)
if suite.countTestCases() != len(expected_names):
raise RuntimeError("unexpected test count")
result = unittest.TextTestRunner(verbosity=2).run(suite)
if result.testsRun != len(expected_names) or not result.wasSuccessful():
raise SystemExit(1)
この確認コードも例であり、実際の標準出力や成功ログは掲載していません。期待どおりなら二件が実行され、通常値3に対する6と、負数に対するValueErrorの両方を確かめる計画です。負数の試験は、例外が起きれば何でもよいのではなく、指定した例外が起きることを求めています。
| 変更する条件 | 期待する観測 | 合わない場合の対応 |
|---|---|---|
| 移行候補をそのまま使う | 二件を指定順に収集し、二件が成功する | 収集名と失敗箇所を分けて確認する |
| testMethodPrefixの設定を外す | 今回のクラスでは選択名が空になり、実行前の照合で止まる | 既定値への戻りを直し、ゼロ件を合格にしない |
| 関数のreturnをcountへ変える | 二件を収集できても通常値の検査が失敗する | 製品側の誤りを修正し、期待値6を緩めない |
| 負数へのValueErrorを削る | 負数の検査が失敗する | 拒否の契約を回復し、例外テストを削らない |
| 順序の設定を逆にする | 名前は同じでも順序照合が合わずに止まる | 意図した変更かを確認し、移行の差分から切り分ける |
| 必要な環境や記録を用意できない | 実測は未確認のまま | 期待結果と実測を分けて移行判定を保留する |
故意の誤りを入れる二つの例は、隔離した試験用コピーで「テストが不具合を検出するか」を確かめる計画です。本番コードを壊して試す手順ではありません。確認後は正常版へ戻したコードの識別情報も残し、不具合を入れた版と配布する版を取り違えないようにします。
テスト件数が正しくても、通常値だけを見て異常入力の拒否が失われていれば互換ではありません。逆に、誤った実装に対してテストが失敗することは、この確認では期待した結果です。「すべて成功」を一つの合格基準にすると、検出能力を確認する試験の意味を取り違えます。
互換性の記録は処理の意味まで残す
移行確認では、旧環境の実測、新環境の実測、今回新しく定めた期待結果を別々に記録します。旧環境の結果が残っていなければ「以前も成功していた」と補わず、利用側の仕様と旧設定から比較項目を作ります。旧APIを呼び出せない新環境で、旧コードの失敗を新APIの不具合として扱わないことも必要です。
記録に必要なのは、コードの版、Pythonの実際の版、ローダー設定、収集した名前と件数、実行件数、成功・失敗・エラー・スキップの内訳、差異への判断です。本例ではスキップのない二件を前提にしているため、件数だけ合っていてもスキップで実行されていないなら理由を調べます。実測がない欄は未実行とします。
読込段階のエラーと、実行したテストの失敗も区別します。クラスを読み込めない状態では、業務処理が正しいかまで判断できません。テスト内の検査が失敗した場合は、入力、期待値、実際の結果を調べます。両者をまとめて「移行APIの問題」とすると、関係のない検査を弱める修正へ進みやすくなります。
また、順序を保つことと、試験が順序へ依存してよいことは別です。今回の変更では比較を明確にするため既存順序を保ちますが、前の試験の残した状態がないと次が通らない場合は、初期化や後片付けを別の問題として見直します。API移行と試験設計の変更を一度に混ぜない方が、差異の原因を追いやすくなります。
旧環境と新環境の両方を支える必要がある場合には、置換候補が両方の対象版で利用できるかも確認します。今回のように新しい呼び方を共通化できるか、版ごとの分岐が本当に必要かを先に判断します。APIがないときだけ旧式へ戻す分岐を増やすと、どの経路を試験したかが分かりにくくなります。分岐を置くなら、利用条件と廃止予定を記録し、それぞれの経路を試験対象へ含めます。
他のAPIへ応用するときは、テスト名や順序に相当する「外から観測できる契約」を選びます。入力値、戻り値の型、既定値、例外、処理対象の範囲などです。たとえば空の一覧を返すことと例外を出すことが変われば、呼び出し側の分岐も変わります。今回の二件の確認をそのまま別APIへ流用せず、利用側が頼っている挙動から項目を組み立てます。
移行後に問題が見つかった際の戻し先も、対象版と一緒に決めます。3.13系へ旧makeSuiteの呼び出しだけを戻しても復旧案にはなりません。戻せるコードと環境の組合せ、変更前の記録、戻す権限を確認します。ここでの説明例はコード移行の判断材料であり、稼働環境の更新や切戻しを実施した記録ではありません。
AIへの修正依頼と採用判断をつなぐ
AIへ移行を依頼する際は、「古いAPIを新しくして」だけでなく、対象版、公式の変更箇所、残す設定、変更を認める範囲、必要な確認を渡します。今回なら「Python 3.13を対象とし、makeSuiteをTestLoaderへ移す。check_の選択と比較関数を維持し、正常と負数拒否のテストは変更しない」と具体化できます。
返ってきたコードでは、API名だけでなく、差分にテストの削除、期待値の変更、例外の握りつぶしが混ざっていないかを読みます。移行のために必要な変更と、挙動を変える提案は分けて評価します。公式資料へリンクが付いていても、その資料が対象版と実際の引数を支えるかは別に照合します。
採用を保留するのは、公式な置換先を確認できない、必要な選択条件が移せない、テストが欠落する、異常時の拒否が変わる、実測が必要なのに未実行、といった場合です。原因を特定するまでは、警告を隠したり失敗するテストを消したりして見かけの成功を作りません。
移行用の記録を残す負担はありますが、次に同じAPIが生成されたときは、確認済みの対象版と置換理由をレビューの材料として再利用できます。ただし、対象版や利用条件が変われば再確認します。API名を禁則語として覚えさせるだけでなく、公式の根拠、意味を保つ差分、実際の試験結果をつなぐことで、古いコードを採用してしまう経路を減らせます。

