Skip to content

Commit

Permalink
FY2024 資料更新: テストプログラミング ハンズオン (#166)
Browse files Browse the repository at this point in the history
* 余計な末尾スペースの削除

* 不要な改行を削除

<br/> がなくても整形されていて十分可読性があるため、削除してみた。

* 「概論」、「準備」について加筆。

講義の約束事、その他自然な日本語になるよう修正した。

* 「同値クラス、境界値テスト」について加筆。

サンプルコードも手元で動かせるように修正。その他、細かな文言などの修正。

* 「APIと関数のモック」について加筆。

サンプルコードも手元で動かせるように修正。その他、パスなど細かい部分の修正。

* 「TDDをやってみる」について加筆。その他、細かな修正 (表現の統一など)

* fastapi テストクライアント用に、requests から httpx に変更

* 資料修正に合わせてサンプルコードを修正。

* 指摘事項取り込み (#166 (comment))
  • Loading branch information
kurizz authored Aug 15, 2024
1 parent 5323777 commit c6df6a9
Show file tree
Hide file tree
Showing 12 changed files with 758 additions and 499 deletions.
1,101 changes: 656 additions & 445 deletions src/server-app/test-hands-on/README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ def test_get_echo_test(self):
self.assertEqual(status, 200)
self.assertEqual(data, {"message": "got the message: hoge"})


def test_get_gacha(self):
with mock.patch.object(challenge, '_exec_gacha', return_value=True):
res = client.get("/gacha")
Expand Down
34 changes: 18 additions & 16 deletions src/server-app/test-hands-on/exercises/answer3/challenge.py
Original file line number Diff line number Diff line change
@@ -1,53 +1,55 @@
from fastapi import FastAPI

################################################################
# 今回の講義ではプログラムの視認性を上げるため、セッションを使用せずに
# サーバの値を保持していますが、実際のプロダクトでこのようにユーザーを
# 識別せずに値を保持すると、セキュリティ・インシデントになるので
# 気をつけてください
################################################################
############################################################################
# 今回の講義ではプログラムの視認性を上げるため、セッションを使用せず値を保持しています。
# 実際のプロダクトコードでこのようにするとセキュリティ事故になり得るので気をつけてください。
# (任意のユーザが読み書きできる値をサーバが保持しており、情報漏洩に繋がる可能性があるため)
############################################################################

current_number = 0
app = FastAPI()


def get_current_number():
return current_number


def set_current_number(number: int):
global current_number
current_number = int(number)

################################################################

# コンソールから実行は以下のコマンド
# $ python3 -m uvicorn challenge:app --reload
app = FastAPI()

@app.get("/")
async def get_index():
def get_index():
return {"current_number": get_current_number()}


@app.get("/add/{number}")
async def get_add(number: int):
def get_add(number: int):
set_current_number(
get_current_number() + number
)
return {"current_number": get_current_number()}


@app.get("/sub/{number}")
async def get_sub(number: int):
def get_sub(number: int):
set_current_number(
get_current_number() - number
)
return {"current_number": get_current_number()}


@app.get("/mul/{number}")
async def get_mul(number: int):
def get_mul(number: int):
set_current_number(
get_current_number() * number
)
return {"current_number": get_current_number()}


@app.get("/div/{number}")
async def get_div(number: int):
def get_div(number: int):
set_current_number(
get_current_number() / number
)
Expand Down
42 changes: 42 additions & 0 deletions src/server-app/test-hands-on/exercises/answer3/test_challenge.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import unittest
from fastapi.testclient import TestClient
from . import challenge

client = TestClient(challenge.app)


class ApiTestCase(unittest.TestCase):
def test_success(self):
res = client.get("/")
self.assertEqual(res.json(), {"current_number": 0})
res = client.get("/add/123")
self.assertEqual(res.json(), {"current_number": 123})
res = client.get("/sub/13")
self.assertEqual(res.json(), {"current_number": 110})
res = client.get("/mul/5")
self.assertEqual(res.json(), {"current_number": 550})
res = client.get("/div/275")
self.assertEqual(res.json(), {"current_number": 2})

client.get("/sub/2")

def test_tdd(self):
res = client.get("/")
data = res.json()
self.assertEqual(data, {"current_number": 0})

res = client.get("/add/10")
data = res.json()
self.assertEqual(data, {"current_number": 10})

res = client.get("/sub/5")
data = res.json()
self.assertEqual(data, {"current_number": 5})

res = client.get("/mul/3")
data = res.json()
self.assertEqual(data, {"current_number": 15})

res = client.get("/div/5")
data = res.json()
self.assertEqual(data, {"current_number": 3})
5 changes: 5 additions & 0 deletions src/server-app/test-hands-on/exercises/answer3/todo_list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- [x] 現在の値を返すエンドポイント
- [ ] 現在の値に、パラメータで指定した値を加算した結果を返すエンドポイント
- [ ] 現在の値に、パラメータで指定した値を減算した結果を返すエンドポイント
- [ ] 現在の値に、パラメータで指定した値を乗算した結果を返すエンドポイント
- [ ] 現在の値に、パラメータで指定した値を除算した結果を返すエンドポイント
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@

class HelloTestCase(unittest.TestCase):
def test_success(self):
self.assertEqual(hello(), "goodbye world?")
self.assertEqual(hello(), "hello iij-bootcamp")
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ def apply(quantity: int):
if not isinstance(quantity, int):
raise TypeError()

if quantity < 10 or 100 < quantity:
if quantity < 10 or 100 < quantity:
return 'not accepted'

return 'accepted'
20 changes: 8 additions & 12 deletions src/server-app/test-hands-on/exercises/exercise2/challenge.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,27 @@
from fastapi import FastAPI
import random


# コンソールから実行は以下のコマンド
# $ python3 -m uvicorn challenge:app --reload
app = FastAPI()


@app.get("/")
async def get_index():
def get_index():
return {"message": "hello world"}


@app.get("/echo/{data}")
async def get_echo(data: str):
return {
"message": "got the message: {0}".format(data)
}
def get_echo(data: str):
return {"message": "got the message: {0}".format(data)}


# 100分の1で当たるガチャ関数
def _exec_gacha():
return random.randrange(0, 100) == 0


@app.get("/gacha")
async def get_gacha():
def get_gacha():
message = "you lose"
if _exec_gacha():
message = "you win"
return {
"message": message
}
return {"message": message}
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,13 @@ def test_get_index(self):
# "/"のパスでアクセスして実行するAPIのテストをしよう
pass


def test_get_echo_test(self):
# 〇〇の値でレスポンスが変わる、"/echo/〇〇"のAPIをテストしよう
pass


def test_get_gacha(self):
# 確率でレスポンスが変動する"/gacha"のAPIをテストしよう
# 関数"challenge._exec_gacha"は、返り値が不定のため、この関数の返り値を固定してみよう

# 返り値の固定は、下記のmock.patch.objectの構文でできるよ
# mock.patch.object(パッケージ, "関数名", return_value="返り値")
# 参考URL: https://docs.python.org/ja/3/library/unittest.mock.html
# `with mock.patch.object(パッケージ, "関数名", return_value="返り値")`
pass
25 changes: 11 additions & 14 deletions src/server-app/test-hands-on/exercises/exercise3/challenge.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,26 @@
from fastapi import FastAPI

################################################################
# 今回の講義ではプログラムの視認性を上げるため、セッションを使用せずに
# サーバの値を保持していますが、実際のプロダクトでこのようにユーザーを
# 識別せずに値を保持すると、セキュリティ・インシデントになるので
# 気をつけてください
################################################################
############################################################################
# 今回の講義ではプログラムの視認性を上げるため、セッションを使用せず値を保持しています。
# 実際のプロダクトコードでこのようにするとセキュリティ事故になり得るので気をつけてください。
# (任意のユーザが読み書きできる値をサーバが保持しており、情報漏洩に繋がる可能性があるため)
############################################################################

current_number = 0
app = FastAPI()


def get_current_number():
return current_number


def set_current_number(number: int):
global current_number
current_number = int(number)

################################################################

# コンソールから実行は以下のコマンド
# $ python3 -m uvicorn challenge:app --reload
app = FastAPI()

@app.get("/")
async def get_index():
def get_index():
return {"current_number": get_current_number()}

# 以下に、テスト駆動開発を使用して、加減乗除を行うAPIのエンドポイントを
# 作成していこう
# 以下に、TDD を使って、加減乗除を行うAPIのエンドポイントを作成していきましょう
17 changes: 14 additions & 3 deletions src/server-app/test-hands-on/exercises/exercise3/test_challenge.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@


class ApiTestCase(unittest.TestCase):
# このテストは変更せず、テストが通るように"challenge.py"を改修しよう
# 仕様通りにAPIを作成すると、このテストは通るようになるよ
# このテストは変更せず、テストが通るように "challenge.py" を改修してみましょう
# 仕様通りにAPIを作成すると、このテストは成功するようになります
def test_success(self):
res = client.get("/")
self.assertEqual(res.json(), {"current_number": 0})
Expand All @@ -20,12 +20,23 @@ def test_success(self):
res = client.get("/div/275")
self.assertEqual(res.json(), {"current_number": 2})

# 以下、加減乗除を行うAPIを作成するため、加算⇒減算⇒乗算⇒除算の順に動作をテストしていこう
client.get("/sub/2")

def test_tdd(self):
# 値の取得のテスト。こちらは既に実装済みですのでテストは成功します
res = client.get("/")
data = res.json()
self.assertEqual(data, {"current_number": 0})

# 以下、加減乗除を行うAPIを作成するため、加算、減算、乗算、除算の順に動作をテストしてみましょう

# サイクル(1) では、この加算のテストが通るように Red, Green, Refactoring をやってみましょう
res = client.get("/add/10")
data = res.json()
self.assertEqual(data, {"current_number": 10})

# 次に、サイクル(2) では、減算のテストを作り、Red, Green, Refactoring をやってみましょう

# 続けて、サイクル (3) では乗算を完成させてみましょう

# 最後に、サイクル (4) では除算を完成させてみましょう
2 changes: 1 addition & 1 deletion src/server-app/test-hands-on/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
fastapi
uvicorn[standard]
requests
httpx

0 comments on commit c6df6a9

Please sign in to comment.