[{"data":1,"prerenderedAt":560},["ShallowReactive",2],{"content-\u002Fcontents\u002Fpytest":3,"surroundPost-\u002Fcontents\u002Fpytest":551},{"id":4,"title":5,"body":6,"createdAt":539,"description":47,"draft":540,"extension":541,"meta":542,"navigation":219,"path":543,"seo":544,"stem":545,"tags":546,"thumbnail":549,"updatedAt":539,"__hash__":550},"contents\u002Fcontents\u002Fpytest.md","pytestの基本的な使い方",{"type":7,"value":8,"toc":529},"minimark",[9,13,16,19,22,27,35,38,48,53,56,67,77,90,173,186,192,196,199,250,253,256,294,301,307,310,314,317,337,344,348,351,375,382,386,389,392,399,446,452,458,461,488,491,497,500,503,506,509,525],[10,11,12],"p",{},"2022 年最後の技術記事は pytest についてです。",[10,14,15],{},"Python を業務で使っていると、テストを書くことは必ずあると思います。",[10,17,18],{},"そんな時にまず候補に上がるのが pytest。",[10,20,21],{},"今回はそんな pytest の基礎的なところを書いていきます。",[23,24,26],"h2",{"id":25},"pytest-を使う準備","pytest を使う準備",[10,28,29,30,34],{},"まずはインストールします。例によって poetry で環境を作るので",[31,32,33],"code",{},"poetry add pytest","します。",[10,36,37],{},"以下は今回のテスト用のディレクトリ構成です。",[39,40,45],"pre",{"className":41,"code":43,"language":44},[42],"language-text",".\n├── poetry.lock\n├── pyproject.toml\n├── src\n│   ├── __init__.py\n│   ├── error.py\n│   ├── module1.py\n│   └── sandbox.ipynb\n└── tests\n    ├── __init__.py\n    └── test_module.py\n","text",[31,46,43],{"__ignoreMap":47},"",[49,50,52],"h3",{"id":51},"pytest-の設定ファイル","pytest の設定ファイル",[10,54,55],{},"pytest を実行する際に必要に応じて使いたい設定を書いておきます。基本的な使い方のみならなくても動作します。",[10,57,58,59,62,63,66],{},"pytest では Python でよくある設定ファイル複数に対応しています。poetry を使っているならば",[31,60,61],{},"pyproject.toml","に書くのが良いでしょう。単純に pytest を使いたいだけなら",[31,64,65],{},"pytest.ini","に書きます。",[10,68,69,70],{},"（参考：",[71,72,76],"a",{"href":73,"rel":74},"https:\u002F\u002Fdocs.pytest.org\u002Fen\u002Flatest\u002Freference\u002Fcustomize.html%EF%BC%89",[75],"nofollow","https:\u002F\u002Fdocs.pytest.org\u002Fen\u002Flatest\u002Freference\u002Fcustomize.html）",[10,78,79,80,85,86,89],{},"よく使う設定だけ書いていきます。出来ることはたくさんあるので、公式サイトの",[71,81,84],{"href":82,"rel":83},"https:\u002F\u002Fdocs.pytest.org\u002Fen\u002Flatest\u002Freference\u002Freference.html#command-line-flags",[75],"Command-line Flags","を参照ください。\nまた別途プラグインのインストールが必要ですが、よく使うのでカバレッジを計測する",[31,87,88],{},"pytest-cov","のフラグも載せておきます。",[91,92,93,106],"table",{},[94,95,96],"thead",{},[97,98,99,103],"tr",{},[100,101,102],"th",{},"フラグ",[100,104,105],{},"説明",[107,108,109,120,130,140,153,163],"tbody",{},[97,110,111,117],{},[112,113,114],"td",{},[31,115,116],{},"-s",[112,118,119],{},"print 文 などを表示できるようになる",[97,121,122,127],{},[112,123,124],{},[31,125,126],{},"-v",[112,128,129],{},"テストケース名、テストケースごとに PASSED や FAILED などの詳細情報が表示される",[97,131,132,137],{},[112,133,134],{},[31,135,136],{},"-x",[112,138,139],{},"テストが失敗した時点でテストを終了する",[97,141,142,147],{},[112,143,144],{},[31,145,146],{},"-m \u003Cmaker名>",[112,148,149,150],{},"指定した maker 名 の デコレータをつけたテストのみ実行される。デコレータは次のように書く",[31,151,152],{},"@pytest.mark.\u003Cmaker名>",[97,154,155,160],{},[112,156,157],{},[31,158,159],{}," --durations=N",[112,161,162],{},"テストケースごとの実行時間、setup や teardown の時間を表示する。N=0 だと全てのテストケースに対して実行される",[97,164,165,170],{},[112,166,167],{},[31,168,169],{},"--cov --cov-branch",[112,171,172],{},"条件分岐のカバレッジ（C1）をみるためのフラグ",[10,174,175,176,178,179,182,183,185],{},"これらを書いた",[31,177,61],{},"は以下になります。",[31,180,181],{},"duration"," や",[31,184,136],{},"は適宜使うので設定ファイル上は一旦除いてます。",[39,187,190],{"className":188,"code":189,"language":44},[42],"[tool.pytest.ini_options]\naddopts = \"--cov --cov-branch -s -v\"\ntestpaths = [\n    \"tests\",\n]\n",[31,191,189],{"__ignoreMap":47},[23,193,195],{"id":194},"pytest-で正常系をテストする場合","pytest で正常系をテストする場合",[10,197,198],{},"簡単な以下の関数をテストしてみます。",[39,200,204],{"className":201,"code":202,"language":203,"meta":47,"style":47},"language-py shiki shiki-themes github-dark","from src.error import NotIntError\n\n\ndef add_one(x: int) -> int:\n    if not isinstance(x, int):\n        raise NotIntError(\"int以外の型です。入力はintのみです\")\n    return x + 1\n","py",[31,205,206,214,221,226,232,238,244],{"__ignoreMap":47},[207,208,211],"span",{"class":209,"line":210},"line",1,[207,212,213],{},"from src.error import NotIntError\n",[207,215,217],{"class":209,"line":216},2,[207,218,220],{"emptyLinePlaceholder":219},true,"\n",[207,222,224],{"class":209,"line":223},3,[207,225,220],{"emptyLinePlaceholder":219},[207,227,229],{"class":209,"line":228},4,[207,230,231],{},"def add_one(x: int) -> int:\n",[207,233,235],{"class":209,"line":234},5,[207,236,237],{},"    if not isinstance(x, int):\n",[207,239,241],{"class":209,"line":240},6,[207,242,243],{},"        raise NotIntError(\"int以外の型です。入力はintのみです\")\n",[207,245,247],{"class":209,"line":246},7,[207,248,249],{},"    return x + 1\n",[10,251,252],{},"後々異常系もテストしたいので、あえて例外を発生させてます。",[10,254,255],{},"そしてこれが正常系のテストコード。",[39,257,259],{"className":201,"code":258,"language":203,"meta":47,"style":47},"from src.module1 import add_one\n\n\ndef test_正常系():\n    result = add_one(1)\n    expect = 2\n    assert result == expect\n\n",[31,260,261,266,270,274,279,284,289],{"__ignoreMap":47},[207,262,263],{"class":209,"line":210},[207,264,265],{},"from src.module1 import add_one\n",[207,267,268],{"class":209,"line":216},[207,269,220],{"emptyLinePlaceholder":219},[207,271,272],{"class":209,"line":223},[207,273,220],{"emptyLinePlaceholder":219},[207,275,276],{"class":209,"line":228},[207,277,278],{},"def test_正常系():\n",[207,280,281],{"class":209,"line":234},[207,282,283],{},"    result = add_one(1)\n",[207,285,286],{"class":209,"line":240},[207,287,288],{},"    expect = 2\n",[207,290,291],{"class":209,"line":246},[207,292,293],{},"    assert result == expect\n",[10,295,296,297,300],{},"実行はターミナルで",[31,298,299],{},"pytest","と打つだけでいけます。ファイルやクラスを指定して実行することもできます。",[39,302,305],{"className":303,"code":304,"language":44},[42],"(.venv) MacBook-Pro pytest-check % pytest\n====================================================================================================== test session starts ======================================================================================================\nplatform darwin -- Python 3.8.7, pytest-7.2.0, pluggy-1.0.0\nrootdir: \u002FUsers\u002Fwork\u002Fpytest-check\ncollected 1 item\n\ntests\u002Ftest_module.py .                                                                                                                                                                                                    [100%]\n\n======================================================================================================= 1 passed in 0.01s =======================================================================================================\n",[31,306,304],{"__ignoreMap":47},[10,308,309],{},"これで正常系のテストができました。",[23,311,313],{"id":312},"pytest-で異常系をテストする場合","pytest で異常系をテストする場合",[10,315,316],{},"続いて異常系です。以下が異常系テストです。",[39,318,320],{"className":201,"code":319,"language":203,"meta":47,"style":47},"def test_異常系():\n    with pytest.raises(NotIntError):\n        _ = add_one(\"1\") # strの数値を渡す\n\n",[31,321,322,327,332],{"__ignoreMap":47},[207,323,324],{"class":209,"line":210},[207,325,326],{},"def test_異常系():\n",[207,328,329],{"class":209,"line":216},[207,330,331],{},"    with pytest.raises(NotIntError):\n",[207,333,334],{"class":209,"line":223},[207,335,336],{},"        _ = add_one(\"1\") # strの数値を渡す\n",[10,338,339,340,343],{},"これで先ほど関数で定義しておいた",[31,341,342],{},"NotIntError","が発生するので、pytest.raises で発生する例外を指定します。",[23,345,347],{"id":346},"pytest-で複数パラメータで実施する","pytest で複数パラメータで実施する",[10,349,350],{},"正常系のテストをかなり適当に書いていたのでもう少し整えてみます。\nここではまず複数のパラメータを試すようにします。",[39,352,354],{"className":201,"code":353,"language":203,"meta":47,"style":47},"@pytest.mark.parametrize(\"val, expect\", [(1, 2), (100, 101), (-1, 0)])\ndef test_正常系(val: int, expect: int):\n    result = add_one(val)\n    assert result == expect\n",[31,355,356,361,366,371],{"__ignoreMap":47},[207,357,358],{"class":209,"line":210},[207,359,360],{},"@pytest.mark.parametrize(\"val, expect\", [(1, 2), (100, 101), (-1, 0)])\n",[207,362,363],{"class":209,"line":216},[207,364,365],{},"def test_正常系(val: int, expect: int):\n",[207,367,368],{"class":209,"line":223},[207,369,370],{},"    result = add_one(val)\n",[207,372,373],{"class":209,"line":228},[207,374,293],{},[10,376,377,378,381],{},"パラメータを複数テストに渡すには",[31,379,380],{},"@pytest.mark.parametrize","のデコレータを使います。\nデコレータに渡すのは引数名と引数のペアのリストです。",[23,383,385],{"id":384},"pytest-の-fixtureフィクスチャー","pytest の fixture（フィクスチャー）",[10,387,388],{},"テストの事前準備と後処理（いわゆる setup, teardown）を実行してくれるのが fixture です。",[10,390,391],{},"よくデータベースに保存されたデータを前提とした処理などに使われます。",[10,393,394,395,398],{},"今回はもっと簡単に",[31,396,397],{},"print","でやってみます。",[39,400,402],{"className":201,"code":401,"language":203,"meta":47,"style":47},"@pytest.fixture()\ndef start_print():\n    print(\"Start Test !!\")\n\n\n@pytest.mark.parametrize(\"val, expect\", [(1, 2), (100, 101), (-1, 0)])\ndef test_正常系(start_print, val: int, expect: int):\n    result = add_one(val)\n    assert result == expect\n",[31,403,404,409,414,419,423,427,431,436,441],{"__ignoreMap":47},[207,405,406],{"class":209,"line":210},[207,407,408],{},"@pytest.fixture()\n",[207,410,411],{"class":209,"line":216},[207,412,413],{},"def start_print():\n",[207,415,416],{"class":209,"line":223},[207,417,418],{},"    print(\"Start Test !!\")\n",[207,420,421],{"class":209,"line":228},[207,422,220],{"emptyLinePlaceholder":219},[207,424,425],{"class":209,"line":234},[207,426,220],{"emptyLinePlaceholder":219},[207,428,429],{"class":209,"line":240},[207,430,360],{},[207,432,433],{"class":209,"line":246},[207,434,435],{},"def test_正常系(start_print, val: int, expect: int):\n",[207,437,439],{"class":209,"line":438},8,[207,440,370],{},[207,442,444],{"class":209,"line":443},9,[207,445,293],{},[10,447,448,449,451],{},"実行すると以下のようになります。（見づらいので",[31,450,126],{},"は使わずに実行してます）",[39,453,456],{"className":454,"code":455,"language":44},[42],"tests\u002Ftest_module.py Start Test !!\n.Start Test !!\n.Start Test !!\n..\n",[31,457,455],{"__ignoreMap":47},[10,459,460],{},"たしかに print 文の内容が表示されています。\n続いて teardown の処理を書いてみます。先ほどの fixture に ß 以下のように yield 句とその後に print 文を追加します。",[39,462,464],{"className":201,"code":463,"language":203,"meta":47,"style":47},"@pytest.fixture()\ndef start_print():\n    print(\"Start Test !!\")\n    yield\n    print(\"Finish Test\")\n\n",[31,465,466,470,474,478,483],{"__ignoreMap":47},[207,467,468],{"class":209,"line":210},[207,469,408],{},[207,471,472],{"class":209,"line":216},[207,473,413],{},[207,475,476],{"class":209,"line":223},[207,477,418],{},[207,479,480],{"class":209,"line":228},[207,481,482],{},"    yield\n",[207,484,485],{"class":209,"line":234},[207,486,487],{},"    print(\"Finish Test\")\n",[10,489,490],{},"以下が実行結果です。",[39,492,495],{"className":493,"code":494,"language":44},[42],"tests\u002Ftest_module.py Start Test !!\n.Finish Test\nStart Test !!\n.Finish Test\nStart Test !!\n.Finish Test\n.\n",[31,496,494],{"__ignoreMap":47},[10,498,499],{},"テストケースの最後に yield 句の後の print 文が実行されていることがわかります。",[23,501,502],{"id":502},"まとめと次回に向けたメモ",[10,504,505],{},"今回は pytest の超基本的なところだけを記載しました。\n実はまだまだ書きたいことがあるので、次回続きを書きたいと思います。",[10,507,508],{},"書くことメモ",[510,511,512,516,519,522],"ul",{},[513,514,515],"li",{},"パラメータの複数組み合わせ方法",[513,517,518],{},"fixtute から fixture を使う方法",[513,520,521],{},"fixture にパラメータを渡す方法",[513,523,524],{},"モックの使い方",[526,527,528],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":47,"searchDepth":216,"depth":216,"links":530},[531,534,535,536,537,538],{"id":25,"depth":216,"text":26,"children":532},[533],{"id":51,"depth":223,"text":52},{"id":194,"depth":216,"text":195},{"id":312,"depth":216,"text":313},{"id":346,"depth":216,"text":347},{"id":384,"depth":216,"text":385},{"id":502,"depth":216,"text":502},"2022-12-31",false,"md",{},"\u002Fcontents\u002Fpytest",{"title":5,"description":47},"contents\u002Fpytest",[547,548],"2022","Python","\u002Fimg\u002Ftwitter-card.png","t7IUMnk0PjTACCWJBdKPJkYTibKbALlnX5_Pn6UQv3c",[552,556],{"title":553,"path":554,"stem":555,"children":-1},"Progressive Disclosure for LLM-Maintained Wiki Knowledge Bases: a Preregistered Ablation","\u002Fcontents\u002Fprogressive-disclosure-llm-wiki-knowledge-bases","contents\u002Fprogressive-disclosure-llm-wiki-knowledge-bases",{"title":557,"path":558,"stem":559,"children":-1},"意外と知らなかったPythonの便利な書き方集","\u002Fcontents\u002Fpython","contents\u002Fpython",1784936718408]