Testing: unittest, pytest, doctest
টেস্টিং — script থেকে software-এ উত্তরণের মূল মানদণ্ড
1. Why Tests?
Code without tests is code you hope works. A test suite tells you when you broke
something, what broke, and lets you refactor fearlessly. Good tests are small, focused,
readable, and fast. Python ships with two testing tools (unittest, doctest); the third
(pytest) is the community standard and installed with pip install pytest.
unittest, doctest); তৃতীয়টি pytest — community standard।
2. Start with Plain assert
def add(a, b):
return a + b
assert add(2, 3) == 5
assert add(-1, 1) == 0
assert add(0, 0) == 0
print("all passed")
assert is the atomic unit of testing. Everything above builds on it. But assert alone
gives you no organization, no reporting, and no way to collect tests across a project — which is why testing
frameworks exist.
3. unittest — Standard Library
import unittest
def add(a, b): return a + b
class TestAdd(unittest.TestCase):
def test_positive(self):
self.assertEqual(add(2, 3), 5)
def test_negative(self):
self.assertEqual(add(-1, -1), -2)
def test_zero(self):
self.assertEqual(add(0, 0), 0)
# Normally: python -m unittest; here we run in-script:
if __name__ == "__main__":
unittest.main(argv=["first"], exit=False)
4. pytest — The Community Favorite
pytest uses plain functions and plain assert. It finds tests automatically (files named
test_*.py, functions test_*) and prints rich failure messages. Install with
pip install pytest, run with pytest.
# pytest is not on Piston, so we simulate it in plain Python
def mul(a, b): return a * b
def test_mul_basic():
assert mul(3, 4) == 12
def test_mul_zero():
assert mul(10, 0) == 0
def test_mul_negative():
assert mul(-2, 5) == -10
# Run them all
for name, obj in list(globals().items()):
if name.startswith("test_") and callable(obj):
obj()
print(f"{name} ✓")
With pytest installed, you would write:
@pytest.fixture
def sample_data():
return [1, 2, 3]
def test_sum(sample_data):
assert sum(sample_data) == 6
@pytest.mark.parametrize("a,b,expected", [
(2, 3, 5),
(0, 0, 0),
(-1, 1, 0),
])
def test_add(a, b, expected):
assert add(a, b) == expected
5. doctest — Tests in Your Docstrings
def factorial(n):
"""Return n!.
>>> factorial(0)
1
>>> factorial(5)
120
>>> factorial(1)
1
"""
result = 1
for i in range(2, n + 1):
result *= i
return result
import doctest
doctest.testmod(verbose=True)
doctests double as documentation. Great for small utility functions; less suitable for complex test scenarios.
6. What Makes a Good Test?
- Fast — a suite that takes a minute gets skipped.
- Isolated — no shared state, no file writes into project directories, no hardcoded
localhost:8080. - Focused — one test per behavior; short and readable.
- Deterministic — same input ⇒ same result. No
datetime.now()orrandomunless seeded. - Automated — runs in CI, not only in developers' heads.
7. Vocabulary (শব্দভাণ্ডার)
| Term | Meaning | বাংলায় |
|---|---|---|
| Unit test | Tests a single function/class in isolation. | একটিমাত্র ফাংশন/class-এর test। |
| Fixture | Reusable test setup data. | Test-এর জন্য reusable setup। |
| Parametrize | Run one test over many input sets. | একই test একাধিক input-এ চালানো। |
| Mock | A fake object standing in for a real one. | আসল object-এর জায়গায় নকল। |
| Coverage | Fraction of code exercised by tests. | Test যে কোড অংশে পৌঁছেছে তার অনুপাত। |
8. Practice Problems
-
Write three
assertstatements to test amax_of_three(a,b,c)function.max_of_three(a,b,c)ফাংশনের জন্য তিনটিassertলিখুন।✨ Show Answer (উত্তর দেখুন)
ans1.pydef max_of_three(a, b, c): return max(a, b, c) assert max_of_three(3, 7, 5) == 7 assert max_of_three(-1, -5, -3) == -1 assert max_of_three(0, 0, 0) == 0 print("passed") -
Use
unittest.TestCaseto test thatsortedreturns a new list.unittest.TestCaseদিয়ে test করুন —sortedএকটি নতুন list return করে।✨ Show Answer (উত্তর দেখুন)
ans2.pyimport unittest class TestSorted(unittest.TestCase): def test_returns_new(self): a = [3, 1, 2] b = sorted(a) self.assertEqual(b, [1, 2, 3]) self.assertEqual(a, [3, 1, 2]) self.assertIsNot(a, b) unittest.main(argv=["x"], exit=False) -
Write a doctest for a function
is_palindrome(s).is_palindrome(s)ফাংশনের জন্য doctest লিখুন।✨ Show Answer (উত্তর দেখুন)
ans3.pydef is_palindrome(s): """Return True iff s reads the same forwards and backwards. >>> is_palindrome("level") True >>> is_palindrome("python") False >>> is_palindrome("") True """ return s == s[::-1] import doctest doctest.testmod(verbose=True) -
Explain why relying on
datetime.now()inside a test can cause flakiness.ব্যাখ্যা করুন — test-এর ভেতরdatetime.now()ব্যবহার কেন flaky।✨ Show Answer (উত্তর দেখুন)
Answer: The result depends on the exact time the test runs, which is non-deterministic. Tests that pass today can fail tomorrow (around DST, leap seconds, a different timezone). Inject the clock as a parameter or use mocking so the value is controlled by the test.
ফলাফল test চলার সঠিক সময়ের উপর নির্ভর করে — non-deterministic। আজ pass করা test কাল fail করতে পারে (DST, leap second, ভিন্ন timezone)। Clock-কে parameter করে inject করুন বা mock করে test-এ মান নিয়ন্ত্রণ করুন।
-
Write one test that verifies a function raises
ValueErroron invalid input.Invalid input-এ ValueError raise হয় কি না — তা test করুন।✨ Show Answer (উত্তর দেখুন)
ans5.pyimport unittest def sqrt_of_positive(x): if x < 0: raise ValueError("negative") return x ** 0.5 class T(unittest.TestCase): def test_raises(self): with self.assertRaises(ValueError): sqrt_of_positive(-4) unittest.main(argv=["x"], exit=False)
Summary — Module 29
Tests are the difference between a script and software. Start with plain assert, graduate to
unittest for structure, and adopt pytest for productive day-to-day work. Add doctests
to tiny utilities. Keep tests fast, isolated, focused, and deterministic — then wire them into CI.
assert, তারপর unittest, এবং পেশাদারী কাজে pytest। ছোট utility-তে doctest। Test রাখুন fast, isolated, focused, deterministic — এবং CI-তে wire করুন।