調べ物した結果

現役SEが仕事と直接関係ないことを調べた結果を。

TheGameをつくる②~Numberクラスの実装_インスタンス生成~

目次

前回のあらすじ


とりあえず開発環境だけできた。

開発環境もう少し見直し


githubにpushしたタイミングでテストを通るようにして、Lintを実行できるようにしました。

name: Python application

on: [push]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v1
    - name: Set up Python 3.7
      uses: actions/setup-python@v1
      with:
        python-version: 3.7
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
    - name: Lint with flake8
      run: |
        pip install flake8
        # stop the build if there are Python syntax errors or undefined names
        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
        # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
        flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
    - name: Test with pytest
      run: |
        pip install -e ./src/BL/
        pip install pytest
        pytest

ウィザードに従ってポチポチするだけでできるから素敵。

pip install -e ./src/BL/

ここだけ、pytest実行できるように自作のパッケージを読み込むようにした。同じようにDockerfileもBuildで読むように

#pytest setup
RUN pip install -e ./src/BL/

を追記。
これで、
githubに上げればGitActionが勝手に環境を作ってテストをしてくれる。
・Dockerでコンテナ立ち上げればパッケージ読み込んだ状態でそく「pytest」をぶち込める環境になる。
が達成できている。
コードメトリクスも収集しといたほうがいいかなぁ。radon放り込むだけだろうから。

ということでRadonうめこみ

末尾に以下を追加した

    - name: Code Metorix
      run: |
        pip install radon
        radon cc -s -e "*test/*" .
        radon mi -s -e "*test/*" .

結果。
gyazo.com
いいでしょう。

Numberクラスのインスタンス


単に数値を渡して、数字を返すだけ。後々Numberにはいろいろ機能を付加しないといけなくなる。
※TheGameは11,22とかぞろ目に意味があったり、僕のかった悪魔の方はほかにも効果があったりする。
ということも踏まえて、単に数値で持つよりクラス化したほうが良さそうなのでクラス化した。
モジュールのイメージはこんな感じ。
gyazo.com
RandomNumber.pyはモジュール名としてふさわしくないと思う。そのうち直すけどいったんこれでいく。
※乱数生成しなきゃ。ってのがずっと頭にあるせいでこんなパッケージ名になってしまった。
※BLはビジネスロジック

先にテストを書いてからコードを作るよ

テストの方の階層はこんな感じ。
gyazo.com

test_RandomNumber.py
#third
import pytest
#user
from BL_main.RandomNumber import *

def test_NumberClass_InvalidException_underNumber():
    '''
    Test argument. under number.
    '''
    with pytest.raises(InvalidArgumentExceptionOfNumber):
        Number.create(1)
    Number.create(2)
def test_NumberClass_InvalidException_overNumber():
    '''
    Test argument. over number.
    '''
    with pytest.raises(InvalidArgumentExceptionOfNumber):
        Number.create(100)
    Number.create(99)
def test_NumberClass_CreateTargetNumber():
    '''
    Test NumberClass initialize. 
    '''
    actual = Number.create(3)
    assert actual.value == 3
    
    actual2 = Number.create(3)
    assert actual == actual2

    actual3 = Number.create(4)
    assert actual != actual3

とりあえず「2~99」の数値しか作れないので、OverとUnderのテスト。
加えて、適当な数字を作って同値比較できるかどうか。までを書いた。

実装はこんな感じ。

RandomNumebr.py
class Number:
    '''
    Number Class.
    '''
    def create(number):
        '''
        Number Class Basic Factory.
        '''
        if (number > 99) :
            raise InvalidArgumentExceptionOfNumber("over number")
        if (number < 2):
            raise InvalidArgumentExceptionOfNumber("under number")
        return Number(number)   
    def __init__(self, number):
        '''
        Constractor.
        '''
        self.value = number
    def __eq__(self, other):
        '''
        Override Equals.
        '''
        if not isinstance(other, Number):
            return NotImplemented
        return self.value == other.value

class InvalidArgumentExceptionOfNumber(Exception):
    pass

ちょっと前まで、pythonの階層とかファイルわけどうしたらいいだろうか。と悩んでたけど、
「.py」は等しくモジュール。ということで腑に落ちた。ひたすらここにクラス書いて問題なさそう。ということでここが膨らんでいくと思われる。
マジックナンバーもあるし、微妙だけどとりあえずこれでいいんじゃなかろう?かかかのか。
Equalsはともかくとして、ゲームとして大小比較はデリケートなところなので(つまり、これから出そうとする数字が今より大きいか小さいか)そこはいったんおきにしておいた。
ゲームのコントロール側を実装するときにまた出てくると思う。

テストは通るけど

Lintの結果がボロボロである。 ここはちまちまなおす。ことにする。
gyazo.com

次回の予定


乱数。乱数つくらないとね。あとNumber「s」クラス。コレクションしとかないとめんどくさそう。
手札、山札、コレクション。うーん。この辺ができたらようやくゲームメインかな。