Error [invalid command 'test']

error: invalid command 'test' の直し方(setuptools 72 で python setup.py test が削除された)

FIX SUMMARY verified
Applies when
python:3.13.14-slimsetuptools 72.0.0 (setup.py test command removed)python setup.py test on setuptools 72+ where the test command was removed (typically a fresh CI env with a newer setuptools)

Verified: reproduced in python:3.13.14-slim, then the invalid command 'test' signature was gone after the fix (exit 0).

CI やローカルで長く使ってきた python setup.py test が、ある日から次のエラーで止まることがあります。

usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
   or: setup.py --help [cmd1 cmd2 ...]
   or: setup.py --help-commands
   or: setup.py cmd --help

error: invalid command 'test'

プロジェクトのコードは何も変えていないのに、テスト実行のコマンドだけが失敗します。原因は、setuptools が 72.0.0 で test コマンドを削除したことです。python setup.py test という起動方法そのものが無くなったので、テストは別の起動方法に移します。setuptools を入れ直しても戻りません。

急いで直すなら、テストランナーを直接呼びます。

# setup.py test の代わりに、標準ライブラリの unittest で実行する
python -m unittest

# pytest を使っているプロジェクトなら
pytest

なぜ起きるのか:setup.py test は非推奨を経て削除された

python setup.py test は、setuptools がテスト実行のために提供していたコマンドです。これは長く非推奨で、実行すると「将来削除される」旨の警告を出し続けていました。予告どおり setuptools 72.0.0 で削除され、test は setuptools が知らないコマンドになりました。

エラーが invalid command 'test'(usage の一覧つき)という形なのは、setup.py が受け付けるコマンドの中に test がもう無いからです。存在しないコマンドを渡したときの一般的な応答で、test に固有の失敗ではありません。

なぜ setuptools 72 以降で出るのか

境界は setuptools の版で、Python の版でもプロジェクトのコードでもありません。setup.py test は setuptools 71 系までは(非推奨の警告つきで)動きます。72.0.0 以降で削除されているので、次のような食い違いで表面化します。

  • CI やコンテナで setuptools が新しく入った:クリーンな環境を作ると、そのとき入手できる新しい setuptools が入ります。ローカルの古い環境に残っている setuptools が 71 以下なら動き、CI だけが 72 以降で invalid command 'test' になります
  • 仮想環境を作り直した/pip install --upgrade setuptools した:更新で 72 の境界を越えると、同じ python setup.py test が通らなくなります。

まず python -c "import setuptools; print(setuptools.__version__)" で版を確認します。72 以降なら、この記事の削除に当たっています。

直し方:テストの起動を setup.py から外す

setup.py test がしていたのは、テストを探して実行することです。同じことは、テストランナーを直接呼べばできます。setuptools のバージョンに依存しません

標準ライブラリの unittest で実行する場合、テストが test_*.py の形なら自動で見つかります。

python -m unittest          # カレント配下の test_*.py を探して実行
python -m unittest discover -s tests   # tests ディレクトリに置いている場合

pytest を使うなら、そのまま呼びます。

pytest

Makefile や CI の設定に python setup.py test が書いてあるなら、その行を上のコマンドに置き換えます。toxnox でテスト環境を定義している場合は、実行コマンドをテストランナー呼び出しに変えます。依存としてのテスト設定setup.pytests_require など、setup.py test が読んでいた項目)も併せて不要になるので、テスト依存は requirements-dev.txtpyproject.toml の任意依存へ移します。

setuptools を入れ直しても戻らない

このエラーで最初に試したくなるのは setuptools の再インストールや更新ですが、それでは戻りません。test コマンドは削除されていて、新しい setuptools にも入っていないからです。更新するほど、削除された状態に近づきます。

古い setuptools(71 以下)に固定すれば setup.py test は動きますが、それは削除前の版に張り付く延命で、非推奨だったコマンドに依存し続けることになります。setuptools は近年、setup.py <command> という直接起動そのものを減らす方向に進んでいるので、テスト起動をランナー側に移すほうが後を引きません。これは、pkg_resources が setuptools 82 で削除されて pip install setuptools では戻らない No module named ‘pkg_resources’ の直し方と同じ形で、「入れ直し」ではなく「削除された機能から移る」のが直し方です。

切り分け(うまくいかないとき)

  • test ではなく別のコマンドで invalid command が出るsetup.py の各種コマンド(ptr などのテスト系プラグインを含む)も、setuptools やプラグインの版で使えなくなることがあります。エラーは同じ「そのコマンドは無い」形で、対処は同じくその機能を提供するツールを直接呼ぶことです。
  • python setup.py 自体に SetuptoolsDeprecationWarning が出るtest に限らず、setup.py を直接コマンド起動すること全体が非推奨として案内されています。ビルドは python -m build、インストールは pip install .、テストはランナー直接、と用途ごとの標準コマンドへ移します。
  • setup.py の中で from setuptools.command.test import test を import している:72.1.0 以降、このモジュール自体は import できる形(スタブ)で残っていますが、test コマンドとしては使えません(実行しても invalid command 'test' の側になります)。import ごと外して、テスト起動をランナーへ移します。
  • python -m unittest でテストが 1 件も見つからない:既定の探索は test_*.py(と TestCase サブクラス)です。ファイル名が違う、tests/__init__.py が無い、といった場合は python -m unittest discover -s <ディレクトリ> -p "<パターン>" で明示します。
  • 手元では通るのに CI でだけ落ちる:setuptools の版が違います。CI のログで実際に入った setuptools の版を確認します。72 以降なら本記事の削除です。

検証環境

  • python:3.13.14-slim、ネットワーク有り
  • 再現:pip install "setuptools==72.0.0" の後、最小の setup.pysetup(name=..., version=...) だけ)に対して python setup.py testerror: invalid command 'test' で終了コード 1
  • 修正:同じ setup.py と同じテスト(unittest.TestCase を1つ)を python -m unittest で実行すると終了コード 0・シグネチャ消滅

再現から修正までは errfix の検証ハーネスが機械的に確認しています。版境界は、setuptools==71.1.0 では同じ python setup.py test が(非推奨の警告つきで)終了コード 0 で通ることを実測して裏づけました。72.0.0 は PyPI で yanked ですが、削除境界を固定して再現するために使っています。削除が 72 以降で継続していること(invalid command 'test' のまま)は、現行の setuptools(83.0.0)でも同じエラーになることを別途実測して確認しました。setup.py test が非推奨を経て 72.0.0 で削除された経緯や、setup.py 直接起動全体の非推奨方針は、setuptools の変更履歴に沿っています。

検証(machine-verified)

この修正は python:3.13.14-slim のバージョン固定コンテナ内で再現し、修正後に invalid command 'test' のシグネチャが消えることを機械で確認しています。

verify — run-case.mjs
$ node run-case.mjs python/setuptools-invalid-command-test
● reproduce invalid command 'test' present ✓
● apply fix exit 0
● re-run invalid command 'test' gone ✓
PASS verified · python:3.13.14-slim · signature gone

確認したのは上のイメージの中だけです。別の環境で直らなかった、記述が違う、という場合は 報告してください(対象と検証イメージは件名・本文に入ります)。