# ===== CodeLab: python-unittest =====
# 以下代码片段按文章出现顺序拼接, 共 8 段

# ----- 片段 1 (python) -----
def discounted_price(price: float, discount: float = 0.9) -> float:
    """返回打折后的价格,discount 是折扣率,如 0.9 表示九折。"""
    if not 0 < discount <= 1:
        raise ValueError("折扣率必须在 (0, 1] 之间")
    return round(price * discount, 2)

# ----- 片段 2 (python) -----
import unittest
from math_utils import discounted_price

class TestDiscountedPrice(unittest.TestCase):
    def test_normal_discount(self):
        self.assertEqual(discounted_price(100), 90.0)

    def test_custom_discount(self):
        self.assertEqual(discounted_price(100, 0.5), 50.0)

    def test_invalid_discount(self):
        with self.assertRaises(ValueError):
            discounted_price(100, 1.5)

if __name__ == "__main__":
    unittest.main()

# ----- 片段 3 (python) -----
import unittest

class TestTips(unittest.TestCase):
    def test_float(self):
        # assertEqual(0.1 + 0.2, 0.3)  # 会失败!
        self.assertAlmostEqual(0.1 + 0.2, 0.3)  # 通过

    def test_container(self):
        self.assertIn("py", "python")
        self.assertNotIn("x", "python")

    def test_raises(self):
        with self.assertRaises(ValueError):
            int("不是数字")

if __name__ == "__main__":
    unittest.main()

# ----- 片段 4 (python) -----
import unittest

class TestScoreBoard(unittest.TestCase):
    def setUp(self):
        # 每个测试开始前都会执行,保证数据是全新的
        self.scores = {"alice": 90, "bob": 85}

    def tearDown(self):
        # 每个测试结束后执行,可在这里释放资源、删临时文件
        self.scores.clear()

    def test_get_score(self):
        self.assertEqual(self.scores["alice"], 90)

    def test_update_score(self):
        self.scores["bob"] = 88
        self.assertEqual(self.scores["bob"], 88)
        # 即便这里改坏了数据,下一个测试的 setUp 也会重建干净数据

if __name__ == "__main__":
    unittest.main()

# ----- 片段 5 (text) -----
project/
├── math_utils.py          # 被测源码
├── main.py
└── tests/
    ├── __init__.py        # 让 tests 成为包,必须存在
    ├── test_math_utils.py
    └── test_main.py

# ----- 片段 6 (bash) -----
# 递归发现 tests/ 下所有测试并运行
python -m unittest discover tests

# 指定运行某个文件,加 -v 显示每个用例的名字
python -m unittest tests.test_math_utils -v

# 运行单个用例
python -m unittest tests.test_math_utils.TestDiscountedPrice.test_normal_discount

# ----- 片段 7 (bash) -----
pip install pytest

# ----- 片段 8 (python) -----
# test_discount.py —— 用 pytest 运行:pytest test_discount.py -v
from math_utils import discounted_price

def test_normal_discount():
    assert discounted_price(100) == 90.0

def test_custom_discount():
    assert discounted_price(100, 0.5) == 50.0

def test_invalid_discount():
    import pytest
    with pytest.raises(ValueError):
        discounted_price(100, 1.5)
