1.TDD覚えた!
2.何かプログラミングしたいなぁ...
3.そういえばpaizaにちょうど問題があるじゃん
4.とりあえず、TDDは置いといてpaizaってどんな問題があるんだろ?
5.やってみた
クリアできなかったTAT
悔しいから、クリアする方法を考える。
<考えたこと>
早さが足りなかった...
ただ早さのために、ソースコードの質は落としたくない
アルゴリズムを考える速度はあまり変わらない気がする
プロセスを見直すべきでは?
・そもそも、普段ブラウザ上でプログラミングしないじゃん
・ついでにTDDによる、小さなサイクルも取り入れたい
ということで、
今回の目標は
ローカル環境でプログラミングし、コピペで提出したい
ところで、ローカル環境でプログラミングするとき、paizaのブラウザ上で行うのと明確に違う部分が一つあります。
完成したプログラムがテストできない!
プログラミングの後半では、入出力のチェックが欠かせないけど、ローカル環境だと、テストできない。
テストケースは、問題に書いてあるけど、いちいち写してられない。
これは簡単に、テストケースをコピペできて、テストができるテストドライバが必要だなーってことでプログラミング。
ファイル構成
main.py - Paizaの課題を書くファイル
mini_test.py - TDD用のユニットテストを書くファイル
testTool.py - テストドライバを書くファイル
test_paiza.py - プログラムの完成を確認するテストを書くファイル
testcase.py - Paizaのテストケースをコピペするファイル
TODOLIST.txt ー 仕様や気になることをメモするファイル
このファイルをコピーして使うことを想定。
基本的に、main.pyとmini_test.py、testcase.txt、TODOLIST.txtは空のファイルで、ここに課題のプログラムを書く感じ。
test_paiza.pyにテストコードを書いて、テストに使うこまごまとした、インプットやアウトプットのメソッドはtestTool.pyにって感じで。
テストの成功判定にはPytestを使います。
まずは、paizaの出題形式を再現しないといけない。
paizaでは標準入力としてinput()の形でデータが流し込まれる。
さらに、その入力は1度ではなく複数回行われる。
例えば、こんな感じの割り算をして、商とあまりを答えさせるプログラムなら。
入力例
5
3
出力例
1
2
と、こんな感じ。pytestでテストを書くとなんとなくこんな感じになりそう。
>test_paiza.py
def test(capsys):
testCase = ["5","3"]
expected = "1\n2\n"
test = main.program(input)
test.start()
out,err = capsys.readouterr()
assert out == expected
なるほどなーと。
とりあえずinputとoutputいちいち手動で写してられないから何とかしないとね。
そもそも、ハードコーディングしているから、依存関係ぶっこ抜かないと。
先にハードコーディングのぶっこ抜きから。
inputMessage = "testCase,expected"
detabase = [(["5","3"],"1\n2\n"]),(["8","2"],"4\n0\n")]
@pytest.mark.parametrize(inputMessage,detabase)
def test(testCase,expected):
test = main.program(input)
test.start()
out,err = capsys.readouterr()
assert out == expected
これで、関数無いの値への依存はなくせた。
そのおかげで1つの関数で複数のテスト値が試せるように。
ただ、まだハードコーディングしているため、このままだと書き写すのに時間がかかってしまう。
テストファイルにコピペして、それを読み込むようにしたいなぁ...
ってことで、どんな感じでデータを扱いたいか決める。
>testcase.txt
5
3
1
2
8
2
4
0
こんな感じなら、すぐにコピペで済みそう。
さてさて、扱うデータの形を決めたら読み込み、変換の関数を...書きました。
(全部書くと長くなるので割愛)
getTestCase()
test_paiza.pyの中身は完成。
>test_paiza.py
import main
import pytest
def getTestCase():
..........#ここに読み込む関数を書く
detabase = getTestCase()
inputMessage = "testCase,expected"
@pytest.mark.parametrize(inputMessage,detabase)
def test(testCase,expected):
test = main.program(input)
test.start()
out,err = capsys.readouterr()
assert out == expected
さてさて、次に考えないといけないのは...
テストができたし、main.pyにどんな感じに課題コードを書くか決めないとなぁ...
さっきの割り算の問題をpaiza環境で書くとこんな感じ
num1 = input()
num2 = input()
print(num1/num2)
print(num1%num2)
ただ、pythonの標準入力関数input()を使っているが、実際にテストするときに手動入力は勘弁願いたい。
というわけで、完全にコピペで済むようにすることはあきらめて、こんな感じにする。
class program:
def __init__(self.input:list):
self.testcase =input
def input(self):
out = self.testcase[0]
self.testcase.pop(0)
return out
def start(self):
num1 = self.input()
num2 = self.input()
print(num1/num2)
print(num1%num2)
これなら、start()の部分をコピペして、selfを消せば大丈夫ですね。
とはいえ、少しだけ見た目が悪い...
コンストラクタとinput()は親クラスに持たせてファイルを分割し、継承する形でprogramクラスを実装しましょう。
>main.py
from testTool import Drive
class program(Drive):
def start(self):
・・・・・
あとは、testTool.pyにDriveクラスを移せば本日の作業終了です。
>testTool.py
class Drive:
def __init__(self.input:list):
self.testcase =input
def input(self):
out = self.testcase[0]
self.testcase.pop(0)
return out
def start(self):
pass
感想
実際に使ってみた感じ、とてもいい感じでした。
Cクラス問題までは、ブラウザで書きなぐったほうが早いですが、Bクラス問題くらいになると、自分の慣れた環境のほうがいいかなぁと。
今回プログラミングのスキルチェックを行う上で、アルゴリズムを考える能力だけが、プログラマーの実力じゃないなと思いました。
paizaスキルチェックでは、速さと正確さが求められている品質です。
これを達成するために、何をしてもいいと考えたとき(カンニングはだめですよ)、自分がどのようなスキルを持っていて、何ができるか、どうやって解決するかを見極めることも実力なのかなぁと思いました。