※当サイトの記事には、広告・プロモーションが含まれます。

Pythonの単体テストは標準ライブラリのunittestを使えば良いか。否、pytestの利用者が多いらしい

www.itmedia.co.jp

 このCMが放映されると、SNSなどに多数の批判が寄せられた。例えばCNNやCNBCのコメンテーターも務めるコンサルタントシェリー・パーマー氏は自身のブログで「これはまさに、絶対にやってほしくないAIの使い方だ」と批判した。「この父親は、娘が自分の言葉で誠実に気持ちを伝えるよう指導するのではなく、人間にとって大切なスキルをAIに頼るよう教えているのだ」と説明した。

Google、Geminiのパリ五輪CMへの不評を受けテレビから撤回 - ITmedia NEWS

⇧ 受け取る側も人間を想定していると思うんだけど、

  • 小説
  • キャッチコピー
  • 画像
  • 動画
  • 音楽

などにおける生成AIの活用は批判的な意見もありつつも許容されているにも拘わらず、「ラブレター」への活用が強く拒絶されるのは、

伝える側: 受け取る側 = 1: 1

⇧ のような関係になりやすいからってことなんだろうか?

いずれについても「創作活動」ということになるとは思いますが、

  • 人間にとっての大切なスキル
  • 生成AIの活用が好ましくない領域

ってのを明確にしないことには、同じ様な過ちが繰り返される気がしますけど...

By the way、

getnews.jp

⇧ パリ五輪(オリンピック)の「抽選ルーレット」で批判続出らしいのですが、疑惑が入り込まないような仕組みを構築すれば良いとは思うのだけど、パリ五輪(オリンピック)運営チームが意図しての炎上狙いなのかね?

パリ五輪(オリンピック)の運営について、「人間中心設計(HCD:Human Centered Design)」を取り入れられなかったのかね?

そもそも、男女混合団体戦を採用した(2021年に開催の東京オリンピックで採用されたらしい)のが謎過ぎる...

全階級同士で試合できる団体戦にすれば良いだけの話な気がしますけど...

仮に全階級同士の試合をするとなった場合、男女7階級あるらしいので、合計14階級らしいので、「七帝柔道」の15人戦より1人少ない14人戦になるから試合時間が長くなるけど、4年間頑張ってきた選手にそれぐらいの時間を与えて上げても良い気がするんですが...

何と言うか、あくまで、競技柔道の話だと思うので、異なる階級で試合させる意味は無い気がしますし...

武道としてみるなら、階級なんて概念は無いですし、「残心」も重要になってくると思いますが...

結局のところ、「お金」の問題ってことですかね...

「聖火」とか神聖なイメージ出してますけど、「お金」が全てということになってるのが哀しいところですね...

まぁ、「予算」は限られていると思いますから、致し方ないのかもしれませんが、「失敗」を次のオリンピックに活かせないものなのかね?

どこに「失敗」があったのかを認識できていないとしたら、まず、「振り返り」をして「失敗」を認識して「課題」を洗い出すところから頑張って欲しいですな...

Python単体テストは標準ライブラリのunittestを使っとけば良いのか

ネットの情報だと、

qiita.com

⇧ とありますように、Pythonの標準ライブラリとしては、「unittest」というものが用意されている模様。

Pythonの公式のドキュメントによると、

docs.python.org

unittest ユニットテストフレームワークは元々 JUnit に触発されたもので、 他の言語の主要なユニットテストフレームワークと同じような感じです。 テストの自動化、テスト用のセットアップやシャットダウンのコードの共有、テストのコレクション化、そして報告フレームワークからのテストの独立性をサポートしています。

https://docs.python.org/ja/3/library/unittest.html

⇧ とあり、Javaの「JUnit」にインスパイアされたものらしい。

ちなみに、

docs.python.org

注釈

test パッケージは Python の内部利用専用です。 ドキュメント化されているのは Python のコア開発者のためです。 ここで述べられているコードは Python のリリースで予告なく変更されたり、削除される可能性があるため、Python 標準ライブラリー外でこのパッケージを使用することは推奨されません。

https://docs.python.org/ja/3/library/test.html

test パッケージには、Python 用の全ての回帰テストの他に、 test.support モジュールと test.regrtest モジュールが入っています。 test.support はテストを充実させるために使い、 test.regrtest はテストスイートを実行するのに使います。

https://docs.python.org/ja/3/library/test.html

test パッケージ内のモジュールのうち、名前が test_ で始まるものは、特定のモジュールや機能に対するテストスイートです。新しいテストはすべて unittest か doctest モジュールを使って書くようにしてください。古いテストのいくつかは、 sys.stdout への出力を比較する「従来の」テスト形式になっていますが、この形式のテストは廃止予定です。

https://docs.python.org/ja/3/library/test.html

⇧ とあるので、Pythonの標準ライブラリで「単体テスト」を実現するのであれば、「unittest」を利用すれば良いということなんですかね?

ちなみに、Pythonにおける「単体テスト」用のライブラリとしては、Python標準のテスト用のライブラリである「unittest」以外にも、

python-guideja.readthedocs.io

⇧ 上記サイト様によりますと、様々なテスト向けのライブラリが存在する模様。

「unittest」の実行結果のカバレッジを取得するには、Pythonの外部ライブラリとVS CodeVisual Studio Code)の拡張機能の追加が必要

Javaの開発でEclipseとか利用している場合は、「カバレッジ」の取得に関するライブラリなどもEclipseに同梱されてるのだけど、「VS CodeVisual Studio Code)」は残念ながら、能動的に「VS CodeVisual Studio Code)」の「拡張機能」をインストールする必要がある模様。

そもそも、Pythonの外部ライブラリのインストールも必要らしい。

qiita.com

⇧ 上記サイト様によりますと、

  1. CodeCoverage
    Pythonの外部ライブラリ
  2. Code Coverage Highlighter
    VS CodeVisual Studio Code)の拡張機能

の2つが必要と。

pytestを使った方が良いという話がある

で、Pythonの標準ライブラリである「unittest」を使っておけば良いのかと思っていたのだけど、

myenigma.hatenablog.com

⇧ 上記サイト様によりますと、「pytest」を使っておいた方が良さそうとのこと。

ネットの情報を漁っていたところ、

  • Python Software Foundation
  • JetBrains

が、毎年、Pythonを利用するエンジニアにアンケートを実施してるらしいので、確認してみました。

■2018年度

www.jetbrains.com

■2019年度

www.jetbrains.com

■2020年度

www.jetbrains.com

■2021年度

lp.jetbrains.com

■2022年度

lp.jetbrains.com

5年間の推移を確認してみると、

年数 pytest unittest
2018 46% 32%
2019 49% 30%
2020 49% 28%
2021 50% 25%
2022 51% 24%

⇧「unittest」の割合は年々、下がっているようです。

とりあえず、「pytest」を使っておいた方が良いということですかね?

「pytest」の実行結果のカバレッジを取得するには、Pythonの外部ライブラリとVS CodeVisual Studio Code)の拡張機能の追加が必要

ネットの情報を漁ったところ、

qiita.com

⇧ 上記サイト様によりますと、

  1. pytest
    Pythonの外部ライブラリ
  2. pytest-cov
    Pythonの外部ライブラリ
  3. Coverage Gutters
    VS CodeVisual Studio Code)の拡張機能

の3つが必要と。「pytest」については、「カバレッジ」に関係なく必要なライブラリ。

ディレクトリの構造は適当で良いのか

前に、

ts0818.hatenablog.com

Pythonのプロジェクトのディレクトリ構成のスタンダードとか無いのか調べても芳しい情報が見当たらなかったのだけど、改めて、考えてみる。

で、JavaMavenやGradleなんかでアプリケーションを作ると、

  • src/main/java
  • src/main/resources
  • src/test/java
  • src/test/resources

みたいな感じでディレクトリの構成を用意してくれるのだけど、Pythonについても同じディレクトリの構成で進めて良いのかが謎。

つまり、

  • src/main/py
  • src/main/resources
  • src/test/py
  • src/test/resources

みたいなディレクトリの構成をスタンダードと考えて良いのか?

基本的に、Pythonのimportの機能がイケていないので、ディレクトリのパスを環境変数「PYTHONPATH」に追加しないといけないので、ある程度、ディレクトリ構成が決まっているとありがたいんだけど、ネットの情報を漁っても、Pythonについてはディレクトリ構成についてのスタンダードってものが無いのよね...

とりあえず、

ts0818.hatenablog.com

⇧ 上記の記事の時に作成したPythonプロジェクトのディレクトリ構成を変更して、以下のような構成にしてみました。

C:\Users\Toshinobu\Desktop\soft_work\python_work\fastapi
│  
├─.venv
│  │  pyvenv.cfg
│  │  
│  ├─Include
│  ├─Lib
│  │  └─site-packages
│  │      │  typing_extensions.py
│  │      │  
│  │      │...省略
│  │              
│  └─Scripts
│          activate
│          activate.bat
│          Activate.ps1
│          deactivate.bat
│          dotenv.exe
│          email_validator.exe
│          fastapi.exe
│          httpx.exe
│          markdown-it.exe
│          pip.exe
│          pip3.12.exe
│          pip3.exe
│          pygmentize.exe
│          python.exe
│          pythonw.exe
│          typer.exe
│          uvicorn.exe
│          watchfiles.exe
│          
├─.vscode
│      launch.json
│      settings.json
│      
└─app
    └─src
        ├─main
        │  ├─py
        │  │  │  main.py
        │  │  │  
        │  │  ├─controller
        │  │  │      user_controller.py
        │  │  │      
        │  │  ├─core
        │  │  │      dependency_inject.py
        │  │  │      
        │  │  ├─entity
        │  │  │      user_entity.py
        │  │  │      
        │  │  ├─repository
        │  │  │      user_repository.py
        │  │  │      
        │  │  └─service
        │  │          user_service.py
        │  │          
        │  └─resources
        └─test
            ├─py
            │  └─service
            └─resources    

Pythonのお作法が分からないので、一旦、Javaでよくあるようなディレクトリ構成にして進めることにします。

VS CodeVisual Studio Code)でpytestを実施してみる

とりあえず、「FastAPI」を利用したPythonのアプリケーションの内、「Serviceレイヤー」に該当する部分は、「pytest」を実施できそうな気がするので、試してみますか。

大前提の要件として、

docs.pytest.org

The pytest framework makes it easy to write small, readable tests, and can scale to support complex functional testing for applications and libraries.

pytest requires: Python 3.8+ or PyPy3.

https://docs.pytest.org/en/stable/

⇧「Python 3.8」以上のバージョンである必要があるので、Pythonのバージョンが旧い場合は、導入できないってことですね。

後は、

docs.pytest.org

In general, pytest is invoked with the command pytest (see below for other ways to invoke pytest). This will execute all tests in all files whose names follow the form test_*.py or \*_test.py in the current directory and its subdirectories. More generally, pytest follows standard test discovery rules.

https://docs.pytest.org/en/stable/how-to/usage.html

⇧ ファイル名の命名規則があるらしく、

  1. test_*.py
  2. \*_test.py

のどちらかでなければならないようです。

いろいろ制約が多いな...

VS CodeVisual Studio Code)」の場合、

redj.hatenablog.com

⇧ 上記サイト様によりますと、

手順としては、

  1. Python仮想環境にログイン
  2. Python仮想環境内での作業
    1. pytestをpipでインストール
    2. pytest-covをpipでインストール
  3. VS CodeVisual Studio Code)での作業
    1. 拡張機能「Coverage Gutters」をインストール
    2. テスト用のファイル作成、テストの処理をコーディング
    3. 「.vscode/settings.json」に設定追加
    4. 「.vscode/launch.json」に設定追加
    5. テストの処理を実行

のような感じ。

■1. Python仮想環境にログイン

VS CodeVisual Studio Code)」の「ターミナル」の「コマンドプロンプト」からでも実行できるのだけど、Windowsの「コマンドプロンプト」を起動して、Python仮想環境にログインします。

 

■2. Python仮想環境内での作業

①pytestをpipでインストール

まずは、「pytest」のインストール。

②pytest-covをpipでインストール

続いて、「pytest-cov」をインストール。

⇧「pytest」「pytest-cov」がインストールできたようです。

■3. VS CodeVisual Studio Code)での作業

拡張機能「Coverage Gutters」をインストール

VS CodeVisual Studio Code)」の「拡張機能」で「Coverage Gutters」を検索してインストール。

 

②テスト用のファイル作成、テストの処理をコーディング

とりあえず、「app/src/test/py/service」ディレクトリ配下に、「.py」ファイルを追加します。

「testpy」の命名規則に則したファイル名にする必要があるので注意。

そしたらば、テストの処理をコーディング。

一応、

qiita.com

qiita.com

⇧ 上記サイト様によりますと、「pytest」で、テストの実行前や、実行後、などに実行させたい処理なども実現できる模様。

「pytest」の公式のドキュメントだと、

docs.pytest.org

⇧5つのスコープがあると。

ただ、テスト実行前に実行させたい処理が複数あって、メソッドなどを分けた場合に、処理順がどうなるかについては、よく分からないですが、

docs.pytest.org

⇧ 上図のような感じで、基本的には、

  1. session
  2. package
  3. module
  4. class
  5. function

の順で処理された後に、テストのメソッドが実行される模様。

話が脱線しましたが、以下のようなテスト処理を記載。

■C:\Users\Toshinobu\Desktop\soft_work\python_work\fastapi\app\src\test\py\service\test_user_service.py

import pytest
#from injector import inject
from app.src.main.py.service.user_service import UserService
from app.src.main.py.entity.user_entity import UserEntity
from app.src.main.py.repository.user_repository import UserRepository
from app.src.main.py.core.dependency_inject import DependencyInjector


class TestUserService:
  
  def __del__(self) -> None:
    print("TestUserServiceのデストラクタを実行")

#   @pytest.fixture(autouse=True)
#   def setup(self):
#     self.user_service = UserService  # UserServiceのインスタンスを作成
#     yield

#   #@inject
#   @pytest.fixture(autouse=True)
#   def execute_before_test(self, user_service: UserService):
#     """
#     テスト実行前処理
#     """
#     self.user_service = user_service
#     yield

  @pytest.fixture(scope='function',autouse=True)
  def execute_before_test(self, request):
    """
    テスト実行前処理
    """
    print("\n")
    print("【テスト開始】"+ str(request.node.name))
    yield

  @pytest.fixture(scope='function',autouse=True)
  def execute_after_test(self, request):
    """
    テスト実行後処理
    """
    yield
    print("【テスト完了】"+ str(request.node.name),end="")
    #del(self)


  ### テスト ###
  # テスト01
  def test_01(self):
    input_user_id: int = 1
    #result: UserEntity = self.user_service.find_user(self.user_service, input_user_id)
    result: UserEntity = DependencyInjector.get_class(DependencyInjector,UserService).find_user(input_user_id)
    # target: UserService = UserService()
    # result: UserEntity = target.find_user(input_user_id)
    #assert result != None

  # テスト02
  def test_02(self):
    assert "OK"

⇧ で保存。

③「.vscode/settings.json」に設定追加

で、「VS CodeVisual Studio Code)」でいろいろ設定が必要のため、設定を追加していきます。

blog.logical.co.jp

⇧ 上記サイト様を参考。

■C:\Users\Toshinobu\Desktop\soft_work\python_work\fastapi\.vscode\settings.json

{
    "python.analysis.extraPaths" : [
        // "${workspaceFolder}"
        // ,"${workspaceFolder}/app"
        // ,"${workspaceFolder}/app/src"
        // ,"${workspaceFolder}/app/src/controller"
        // ,"${workspaceFolder}/app/src/entity"
        // ,"${workspaceFolder}/app/src/repository"
        // ,"${workspaceFolder}/app/src/service"
    ],
    "terminal.integrated.env.windows": {
        "PYTHONPATH": "${workspaceFolder};${workspaceFolder}/app"
    },
    "terminal.integrated.env.linux": {
        "PYTHONPATH": "${workspaceFolder}:${workspaceFolder}/app"
    },
    // pytestの設定
    "python.testing.unittestEnabled": false,
    "python.testing.pytestEnabled": true,
    "python.testing.pytestArgs": [
        "--cov=."
        ,"--cov-report"
        ,"xml"
        ,"--capture=no"
    ],
    "coverage-gutters.showGutterCoverage": false,
    "coverage-gutters.showLineCoverage": true
}

⇧ で保存。

 

④「.vscode/launch.json」に設定追加

何やら「デバッグ」とバッティングしてしまうらしいので、「.vscode/launch.json」に設定を追加。

■C:\Users\Toshinobu\Desktop\soft_work\python_work\fastapi\.vscode\launch.json

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python Debugger: FastAPI",
            "type": "debugpy",
            "request": "launch",
            "cwd": "${workspaceFolder}/app/src/main/py",
            "purpose": ["debug-test"],
            "env": {
              "PYTHONPATH": "${workspaceFolder};${workspaceFolder}/app"   
              ,"PYTEST_ADDOPTS": "--no-cov"                   
            },
            "module": "uvicorn",
            "args": [
                "main:app",
                "--reload"
            ],
            "jinja": true
        }
    ]
}    

⇧ で保存。

➄テストの処理を実行

VS CodeVisual Studio Code)」の「Testing」のアイコンを押下して、テスト処理を記述した「.py」ファイルが認識されていればOK。

実行する。

で、「カバレッジ」については、「VS CodeVisual Studio Code)」の下の方に「Watch」って部分を押下すると、

表示できるらしい。

とりあえず、「pytest」でテスト対象クラスを「依存性注入(DI:Dependency Injection)」する方法とかについての情報が見当たらなかったりで、Pythonにおける「単体テスト」の基本形がサッパリ分かりませんな...

毎度モヤモヤ感が半端ない…

今回はこのへんで。