2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

プログラミングゲームの解答コードを投稿しよう! の関連記事です.

難しいプログラミング問題に取り組むとき,手元での確認・検証は重要です.
この記事では,自分のpaizaプログラミング用のローカルテスト環境を紹介します.
プログラム言語はPythonです.Rank D問題:カジノを例として取り上げます.


2行での要約:
test_main.pyinput1output1input2output2def main()の中身を書き換えて,
ローカル環境でテストやデバッグ実行しよう.

出題・解答 環境

最初に,出題・解答 環境を確認します.
電脳少女はpaizaと画面構成など一部異なりますが,以下の要素を共通して持ちます.

  • 問題の概要
  • 入力される値
  • 期待する出力
  • 条件
  • 入力例1
  • 出力例1
  • 入力例2
  • 出力例2

また,Python3の「解答コード入力欄」は,最初に以下が書かれています.

解答コード入力欄
# coding: utf-8
# 自分の得意な言語で
# Let's チャレンジ!!
input_line = input()
print("XXXXXX")

そして,「提出前動作確認」ボタンを押すと,アウトプットが XXXXXX だと確認できます.
提出前動作確認では,標準入力に対して「入力例1」が与えられ,「出力例1」を標準出力する事が期待されています.自由な入出力での動作確認(テスト)はできません.

この標準入力stdinと標準出力stdoutをどう扱うかが,ローカルテストの鍵となります.

オンラインテスト環境

オンラインテスト環境は,paiza.IOを使うのがベストでしょう.
他にもいろいろなサービスがありますが,公式がpaiza.IOを推奨しています.
標準入力と標準出力は,ブラウザの後ろ側で良い感じに対応されています.

ローカルテスト環境

準備

まず,Pythonが既に実行可能であることを前提とします.
ローカルテストにはpytestを利用します.
pip install pytestpytestがインストールできます.
必要に応じて以下のコマンドを実行し,pytestのバージョンが表示されることを確認してください.

$ python --version
Python 3.12.4
$ pytest --version
pytest 8.3.4
$ python -m pytest --version
pytest 8.3.4

次に,テストファイル test_main.py を用意します.

test_main.py
import pytest


input1 = """
入力例1
""".strip()

output1 = """
出力例1
""".strip()

input2 = """
入力例2
""".strip()

output2 = """
出力例1
""".strip()


def main():
    解答


# 以下は固定
def test_main(monkeypatch) -> None:
    check(monkeypatch, main, input1, output1)
    check(monkeypatch, main, input2, output2)


def check(monkeypatch, func: None, input: str, output: str) -> None:
    import io

    stdin = io.StringIO(input)
    stdout = io.StringIO()

    with monkeypatch.context() as m:
        m.setattr("sys.stdin", stdin)
        m.setattr("sys.stdout", stdout)
        func()

    assert stdout.getvalue() == output + "\n"


if __name__ == "__main__":
    pytest.main([__file__])

このファイルは,すべての問題で使いまわします.
問題毎にファイルの中身を書き換えて,ローカルテストを実行します.

使い方

準備したテストファイル test_main.py の日本語部分を適切なものに書き換えます.
Rank D問題:カジノの場合,ファイルを以下のように書き換えます.

test_main.py
import pytest


input1 = """
- 入力例1
+ 1
+ 5
+ 2
""".strip()

output1 = """
- 出力例1
+ 46
""".strip()

input2 = """
- 入力例2
+ 8
+ 2
+ 80
""".strip()

output2 = """
- 出力例1
+ 818
""".strip()


def main():
-   解答
+   print(sum(i * int(input()) for i in [1, 5, 10]))


# 以下は固定
~~~省略~~~

そして,以下のどちらかのコマンドでテストを実行します.

pytest test_main.py
or
python test_main.py

緑の文字で1 passed in 0.02sのように表示されたら,テストは成功です.
赤の文字で1 failed in 0.15sのように表示されたら,テストは失敗です.
その場合,short test summary infoなど,失敗に関する情報が表示されます.

実際の使い方としては,まず最初に問題の入出力例をブラウザからコピペします.
そして,解答をmain関数の中に書きます.解答の試行錯誤は,main関数内で実施します.
オンライン環境より実行速度が速いので,頻繁にテストを実行しても問題ありません.
解答がテストを通ったら,それをブラウザに張り付けて,提出します.

自分の場合,ブラウザでは「解答コード入力欄」も含め,文字のタイピングは一切行いません.
コピーとペーストだけ実施し,タイピングは test_main.py に対してのみ実施しています.

test_main.py の解説

ここからは,test_main.py を解説します.

test_main.py は,以下の設計方針,自身の要望に基づいて作成されています.

  • すべてを1ファイルで完結させる
  • 編集箇所をなるべく近くでまとめる
  • ローカルテスト環境と本番のブラウザとのやり取りは,コピペのみで済ませる
  • 入出力例に対する改変を不要にする(タブ,インデント,改行,[]の有無など)
  • 入出力例1,2を2箇所以上に書かない

この設計に基づき, test_main.py の書き換え部分は,現在のかたちになりました.

すべてを1ファイルで完結させるとすると,入力例と出力例は,変数にする必要があります.
複数行の入力例をそのままコピペするには,以下の書き方がベストでした.

input1 = """
入力例1
""".strip()

入力例と出力例の間に3行が挟まりますが,これは許容範囲としました.

試行錯誤が必要な解答は,解答をmain関数内で書くことにしました.
余分なインデントが入りますが,コピペで解答を提出できたため,良しとしました.

変数input1output1input2output2main関数ができたら,これらを結びつける処理が必要です.
最終的に,pytestmonkeypatchを利用するのがベストだと判断しました.
monkeypatchを使うと,sys.stdinsys.stdoutを差し替えることが出来ます.

テストの核心部分は,check関数として汎用的にまとめました.

def check(monkeypatch, func: None, input: str, output: str) -> None:
    import io

    stdin = io.StringIO(input)
    stdout = io.StringIO()

    with monkeypatch.context() as m:
        m.setattr("sys.stdin", stdin)
        m.setattr("sys.stdout", stdout)
        func()

    assert stdout.getvalue() == output + "\n"

check関数は,monkeypatch,関数func,入力例input,出力例outputが入力になります.
出力はありませんが,内部でテストを実施します.
内部では,最初にioモジュールをインポートして,代替のstdinstdoutを作成します.
stdinには,inputをセットします.
そして,monkeypatch.context()を利用し,sys.stdinsys.stdoutをそれぞれstdinstdoutに置き換え,funcを実行します.
最後に,funcの標準出力をstdout.getvalue()で受け取り,それがoutputと一致するかテストします.

test_main関数は,check関数へ渡す関数と変数を制御します.

def test_main(monkeypatch) -> None:
    check(monkeypatch, main, input1, output1)
    check(monkeypatch, main, input2, output2)

以下は,常にpytest経由でmain関数を呼び出すための設定です.

if __name__ == "__main__":
    pytest.main([__file__])

2行目をmain()に変えると,直接main関数を呼び出すようになります.
変更後にpython test_main.pyを実行すると,多くの場合に標準入力待ちとなります.

VS Code

ローカルテスト環境の一部として,エディタも紹介します.
自分はエディタとしてVS Codeを使っており,デバッグ実行(F5)をよく利用しています.
ブレークポイントを設定することで,プログラムの途中経過がわかり,とても便利です.
DEBUG CONSOLEは,変数の中身を表示可能で,簡単な計算も実行できるため,これも便利です.
VS Codeなら,以下の表示が可能です.

image.png

終わりに

自分のpaiza用ローカルテスト環境を紹介しました.
オンラインのコーディング環境も便利になってきていますが,まだ,ローカル環境の方が便利だと考えています.
是非, test_main.py をコピーして使ってみてください.

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?