یک برنامه‌نویس در طول روز ده‌ها تصمیم کوچک می‌گیرد. یک تابع اضافه می‌کند، یک شرط تغییر می‌دهد، یک باگ رفع می‌کند. هر بار هم نگران یک چیز است: «آیا این تغییر، جای دیگری از کد را خراب کرد؟»
تست‌نویسی دقیقاً جواب همین نگرانی است.
تست در توسعه نرم‌افزار یعنی نوشتن کدی که صحت کد دیگری را بررسی می‌کند. این تعریف ساده است، اما اثرش عمیق. وقتی برای پروژه‌ات مجموعه‌ای از تست‌ها داری، هر تغییر در کد را می‌توانی با خیال آسوده اعمال کنی؛ چون در کمتر از چند ثانیه مطمئن می‌شوی همه‌چیز درست کار می‌کند.

تست دستی در برابر تست خودکار

اکثر برنامه‌نویسان تازه‌کار بدون اینکه بدانند، تست می‌نویسند. برنامه را اجرا می‌کنند، یک مقدار وارد می‌کنند و خروجی را با چشم چک می‌کنند. به این روش تست دستی می‌گویند. تا وقتی پروژه کوچک است، این روش جواب می‌دهد.
مشکل از جایی شروع می‌شود که پروژه بزرگ‌تر می‌شود. اگر ۵۰ تابع داشته باشی و یکی را تغییر دهی، باید ۴۹ تابع دیگر را هم دستی چک کنی که خراب نشده باشند. این کار نه فقط وقت‌گیر است، بلکه مغز انسان توانایی دیدن همه خطاها را ندارد و اشتباه می‌کند.
تست خودکار این مشکل را حل می‌کند. یک بار کد تست را می‌نویسی، بعد هر وقت خواستی با یک دستور ساده، پایتون همه چیز را در کسری از ثانیه بررسی می‌کند.

انواع تست در توسعه نرم‌افزار

سه نوع تست اصلی وجود دارد که باید بشناسی:

تست واحد (Unit Test) کوچک‌ترین واحد کد را جداگانه بررسی می‌کند. مثلاً فقط یک تابع. سریع است و پیدا کردن مشکل در آن آسان است. در این دوره بیشتر با همین نوع کار می‌کنیم.

تست یکپارچگی (Integration Test) بررسی می‌کند که چند بخش مختلف برنامه در کنار هم درست کار می‌کنند یا نه. مثلاً آیا تابع ثبت‌نام کاربر با پایگاه داده درست ارتباط برقرار می‌کند؟

تست سرتاسری (End-to-End Test) کل مسیر را از ابتدا تا انتها شبیه‌سازی می‌کند؛ مثل اینکه یک کاربر واقعی وارد سایت شود، خرید کند و پیام تأیید دریافت کند.

pytest؛ ابزار استاندارد تست‌نویسی در پایتون

پایتون چند ابزار برای تست‌نویسی دارد. قدیمی‌ترین آن‌ها unittest است که از سال ۲۰۰۱ همراه پایتون بوده. اما امروز اکثر توسعه‌دهندگان حرفه‌ای از pytest استفاده می‌کنند.
دلیلش ساده است: pytest کد کمتری نیاز دارد، خطاها را شفاف‌تر نشان می‌دهد و قابلیت‌های پیشرفته‌تری دارد. طبق آمار سایت JetBrains در گزارش سال ۲۰۲۴، بیش از ۷۰٪ توسعه‌دهندگان پایتون که تست می‌نویسند، از pytest استفاده می‌کنند.

جایگاه تست‌نویسی در بازار کار

تست‌نویسی دیگر یک مهارت جانبی نیست. در اکثر آگهی‌های استخدام توسعه‌دهنده پایتون، آشنایی با pytest یا تست‌نویسی به صورت مستقیم ذکر شده است. شرکت‌هایی مثل Google، Mozilla و Dropbox از pytest در پروژه‌های اصلی‌شان استفاده می‌کنند.
در این دوره با یک پروژه واقعی شروع می‌کنیم: یک سیستم کیف پول دیجیتال می‌سازیم و قدم به قدم یاد می‌گیریم چطور با pytest مطمئن شویم که هر بخش آن درست کار می‌کند.

تست دستی در برابر تست خودکار

برنامه‌نویسی یاد گرفتی، کد نوشتی، اجرا کردی و دیدی که خروجی درست است. این دقیقاً همان تست دستی است. ساده، آشنا و در نگاه اول کافی.

اما یک سوال: اگر فردا یک تابع دیگر هم اضافه کنی، باید دوباره همه چیز را از اول چک کنی؟

تست دستی چطور کار می‌کند؟

تست دستی یعنی یک انسان، برنامه را اجرا می‌کند، ورودی می‌دهد و خروجی را با چشم بررسی می‌کند. هیچ ابزار اضافه‌ای لازم نیست. هیچ کد خاصی نباید نوشته شود.

در پروژه‌های کوچک این روش جواب می‌دهد. یک صفحه، چند تابع، چند دقیقه بررسی. تمام.

مشکل از یک نقطه مشخص شروع می‌شود: رشد پروژه.

وقتی پروژه بزرگ می‌شود، تست دستی شکست می‌خورد

فرض کن یک سیستم کیف پول دیجیتال داری با ۴۰ تابع مختلف. امروز تابع «انتقال وجه» را تغییر دادی. حالا باید ۳۹ تابع دیگر را هم چک کنی که این تغییر چیزی را خراب نکرده باشد.

این کار چند ساعت طول می‌کشد. مغز خسته می‌شود. یک خطای کوچک از دید انسان پنهان می‌ماند. طبق آمار، تست دستی در حال حاضر زمان‌برترین بخش در چرخه توسعه نرم‌افزار است و ۳۵٪ از تیم‌ها آن را به عنوان اصلی‌ترین گلوگاه کاری خود معرفی کرده‌اند.

سه مشکل اصلی تست دستی اینجاست:

خطای انسانی: مغز انسان بعد از بررسی ده‌ها خروجی یکسان، شروع به نادیده گرفتن جزئیات می‌کند. این یک واقعیت بیولوژیکی است، نه ضعف شخصی.

غیرقابل تکرار بودن: امروز یک مسیر را تست کردی. فردا ممکن است همان مسیر را دقیقاً به همان شکل طی نکنی و یک سناریو جا بیفتد.

کندی: هر بار که کد تغییر می‌کند، باید از صفر شروع کنی.

تست خودکار چه فرقی دارد؟

تست خودکار یعنی یک بار کد تست را می‌نویسی. بعد هر وقت خواستی، با یک دستور ساده، پایتون همه چیز را در کمتر از یک ثانیه بررسی می‌کند.

نه خستگی. نه فراموشی. نه جا افتادن سناریو.

ابزارهای تست خودکار می‌توانند نرخ شناسایی خطا را تا ۹۰٪ نسبت به تست دستی بالا ببرند. این عدد بزرگی است. یعنی باگ‌هایی که قبلاً به محیط واقعی می‌رسیدند و کاربران با آن‌ها روبرو می‌شدند، حالا قبل از انتشار شناسایی می‌شوند.

پس تست دستی کاملاً بی‌فایده است؟

نه. این دو روش جایگزین هم نیستند.

تست دستی برای چیزهایی مثل بررسی ظاهر یک صفحه، تجربه کاربری یا سناریوهای خلاقانه‌ای که از ذهن ماشین رد نمی‌شود، هنوز ارزش دارد. امروز ۶۶٪ از تیم‌های توسعه نرم‌افزار از ترکیب هر دو روش استفاده می‌کنند.

اما منطق برنامه، محاسبات، شرط‌ها و رفتار توابع؛ اینها دقیقاً همان جایی است که تست خودکار باید مسئولیت را بگیرد.

در این دوره کجا هستیم؟

پروژه‌ای که با هم می‌سازیم یک کیف پول دیجیتال است. متدهایی مثل واریز، برداشت و انتقال وجه دارد. هر کدام می‌توانند در شرایط خاص رفتار اشتباهی داشته باشند.

تست خودکار با pytest به ما اجازه می‌دهد همه این شرایط را یک بار تعریف کنیم و بعد با هر تغییر در کد، مطمئن شویم هیچ چیز خراب نشده.

انواع تست در توسعه نرم‌افزار

نرم‌افزار مثل یک ساختمان است. قبل از اینکه ساکنان وارد شوند، مهندسان سازه را از چند سطح مختلف بررسی می‌کنند. اول آجر به آجر، بعد دیوار به دیوار، در نهایت کل بنا. تست نرم‌افزار هم دقیقاً همین منطق را دارد.

سه نوع تست اصلی وجود دارد که هر توسعه‌دهنده‌ای باید بشناسد.

تست واحد (Unit Test)

کوچک‌ترین بخش قابل بررسی در یک برنامه را تست واحد ارزیابی می‌کند. این بخش معمولاً یک تابع یا یک متد است. فرض کن یک تابع داری که دو عدد را جمع می‌کند. تست واحد فقط همین یک تابع را می‌گیرد، به آن اعداد مختلف می‌دهد و بررسی می‌کند که خروجی درست است. نه بیشتر، نه کمتر. سرعت اجرای تست واحد بسیار بالاست. صدها تست در چند ثانیه اجرا می‌شوند. همین سرعت است که باعث می‌شود توسعه‌دهندگان بتوانند بعد از هر تغییر کوچک، بلافاصله نتیجه را ببینند. در این دوره، بیشترین وقت را روی همین نوع تست می‌گذاریم.

تست یکپارچگی (Integration Test)

یک برنامه واقعی از قطعات مختلف تشکیل شده. تابع ثبت‌نام با پایگاه داده صحبت می‌کند. سیستم پرداخت با درگاه بانک ارتباط دارد. تست یکپارچگی بررسی می‌کند که این قطعات در کنار هم درست کار می‌کنند. تفاوت تست یکپارچگی با تست واحد اینجاست: تست واحد می‌پرسد «آیا این تابع درست کار می‌کند؟» اما تست یکپارچگی می‌پرسد «آیا این تابع با پایگاه داده به درستی ارتباط برقرار می‌کند؟»

یک مثال ملموس: تابع withdraw در کیف پول دیجیتال ما، موجودی را از پایگاه داده می‌خواند، کسر می‌کند و ذخیره می‌کند. تست واحد فقط محاسبه را چک می‌کند. تست یکپارچگی کل این مسیر را از ابتدا تا انتها بررسی می‌کند.

تست سرتاسری (End-to-End Test)

تست سرتاسری رفتار یک کاربر واقعی را در محیط کامل برنامه شبیه‌سازی می‌کند. مثلاً یک کاربر وارد سایت می‌شود، کیف پول خود را شارژ می‌کند، یک تراکنش انجام می‌دهد و پیام تأیید دریافت می‌کند. تست سرتاسری تمام این مسیر را یکجا بررسی می‌کند. این نوع تست واقعی‌ترین شبیه‌سازی از تجربه کاربر است. اما یک معامله دارد: کند است، نگهداری‌اش سخت است و اجرایش منابع زیادی می‌خواهد. به همین دلیل توصیه می‌شود تعداد تست‌های سرتاسری را کم نگه داری و بیشتر به تست‌های واحد و یکپارچگی تکیه کنی.

چرا شناختن این سه نوع مهم است؟

هر نوع تست یک سوال متفاوت می‌پرسد. وقتی یک باگ پیدا می‌کنی، نوع تست نشان می‌دهد مشکل کجاست. تست واحد قرمز شد؟ مشکل درون یک تابع خاص است. تست یکپارچگی شکست خورد ولی تست واحد سبز است؟ احتمالاً ارتباط بین دو بخش اشکال دارد. تست سرتاسری قرمز شد اما بقیه سبز هستند؟ مشکل در جریان کلی تجربه کاربر است.

این تفکیک وقتی پیدا کردن باگ اهمیت دارد. هرچه باگ دیرتر کشف شود، هزینه رفع آن بیشتر می‌شود؛ یافتن یک خطا در مرحله تولید می‌تواند ۳۰ تا ۱۰۰ برابر گران‌تر از یافتن آن در مرحله توسعه باشد. تست واحد ارزان‌ترین و سریع‌ترین راه برای گرفتن باگ‌ها قبل از اینکه به کاربر برسند است.

در این دوره روی تست‌های واحد تمرکز داریم. متدهایی مثل `deposit`، `withdraw` و `transfer` هر کدام رفتارهای مستقلی دارند که باید جداگانه بررسی شوند. وقتی این پایه محکم شد، مسیر برای نوشتن تست‌های یکپارچگی هم هموار می‌شود.

هرم تست چیست؟

دانستن انواع تست کافی نیست. سوال مهم‌تر اینجاست: چه تعداد از هر نوع باید داشته باشیم؟ یک تیم توسعه می‌تواند صد تست سرتاسری بنویسد و فقط ده تست واحد. از نظر فنی اشکالی ندارد. اما این تیم هر بار که کد را تغییر می‌دهد، ساعت‌ها منتظر می‌ماند تا تست‌ها اجرا شوند. پیدا کردن باگ هم کابوس می‌شود. هرم تست دقیقاً برای جلوگیری از این اشتباه طراحی شده است.

هرم تست از کجا آمد؟

مفهوم هرم تست در سال ۲۰۰۹ توسط Mike Cohn در کتاب «Succeeding with Agile» معرفی شد و بعدها توسط Martin Fowler در یک پست وبلاگی معروف در سال ۲۰۱۲ گسترش پیدا کرد. امروز این مدل در تقریباً تمام تیم‌های توسعه نرم‌افزار حرفه‌ای به عنوان یک استاندارد شناخته می‌شود. ایده اصلی ساده است: شکل یک هرم را تصور کن. پایه‌اش پهن، وسطش باریک‌تر، نوکش تیز.

لایه‌های هرم

پایه هرم: تست‌های واحد

پایین‌ترین و پرتعدادترین لایه. این تست‌ها سریع هستند، ارزان هستند و نوشتنشان آسان است. هر بار که یک تابع تغییر می‌کند، این تست‌ها در کمتر از یک ثانیه نتیجه می‌دهند.

وسط هرم: تست‌های یکپارچگی

تعدادشان از تست واحد کمتر است. کندتر اجرا می‌شوند چون باید چند بخش با هم کار کنند. اما همین کار مشترک را بررسی می‌کنند؛ چیزی که تست واحد نمی‌تواند.

نوک هرم: تست‌های سرتاسری

کمترین تعداد را دارند. کامل‌ترین تصویر را می‌دهند، اما گران‌ترین و کندترین هستند. نوشتن و نگهداری‌شان وقت زیادی می‌برد.

نسبت استاندارد چقدر است؟

نسبت کلاسیک که اکثر تیم‌های حرفه‌ای از آن استفاده می‌کنند تقریباً ۷۰٪ تست واحد، ۲۰٪ تست یکپارچگی و ۱۰٪ تست سرتاسری است. این اعداد ثابت نیستند. پروژه‌های مختلف نیازهای متفاوتی دارند. اما جهت کلی تغییر نمی‌کند: بیشتر تست واحد، کمتر تست سرتاسری.

چرا این شکل هرمی؟

هدف هرم تست این است که یک حلقه بازخورد سریع داشته باشیم، هزینه تست را پایین نگه داریم و در عین حال پوشش خوبی از کد داشته باشیم. یک مثال ملموس: اگر در پروژه کیف پول دیجیتال فقط تست سرتاسری داشتیم، هر بار که متد withdraw را تغییر می‌دادیم، باید منتظر می‌ماندیم یک کاربر فرضی وارد سیستم شود، موجودی را چک کند، برداشت کند و پیام تأیید دریافت کند. این فرآیند ثانیه‌ها طول می‌کشد. با صد تست سرتاسری، اجرا چند دقیقه می‌شود. با هزار تست، یک ساعت. تست واحد همان سناریو را در چند میلی‌ثانیه چک می‌کند.

وقتی هرم برعکس می‌شود

وقتی تیم‌ها بیشتر روی تست‌های سرتاسری تمرکز می‌کنند تا تست واحد، هرم برعکس می‌شود و شکل یک مخروط وارونه پیدا می‌کند. این وضعیت نشانه یک مشکل جدی است. تست‌های سرتاسری شکننده هستند. یک تغییر کوچک در رابط کاربری می‌تواند ده‌ها تست را قرمز کند، در حالی که اصل منطق برنامه دست‌نخورده است. پیدا کردن دلیل قرمز شدن هم وقت زیادی می‌برد.

تمام تست‌هایی که در این دوره می‌نویسیم در پایه هرم قرار دارند؛ تست‌های واحد با pytest. این لایه محکم‌ترین پایه برای هر پروژه پایتونی است و یادگیری درست آن، نوشتن لایه‌های بالاتر را هم آسان‌تر می‌کند.

pytest در مقایسه با unittest

پایتون از سال ۲۰۰۱ یک ابزار تست‌نویسی داخلی دارد که بدون نیاز به نصب چیز اضافه‌ای در دسترس است. اسمش unittest است. این ابزار از نسخه ۲.۱ پایتون بخشی از کتابخانه استاندارد شده و هر کسی که پایتون نصب کرده، بدون هیچ کار اضافه‌ای به آن دسترسی دارد.  پس چرا باید سراغ pytest برویم؟ برای پاسخ دادن به این سوال، بهتر است مستقیم با کد شروع کنیم.

همان تست، دو روش متفاوت

فرض کن می‌خواهی متد deposit در کیف پول دیجیتال را تست کنی. با unittest کد به این شکل است: 

import unittest
from wallet import DigitalWallet

class TestWallet(unittest.TestCase):
    def test_deposit_increases_balance(self):
        wallet = DigitalWallet("علی", 50.0)
        wallet.deposit(30.0)
        self.assertEqual(wallet.balance, 80.0)

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

همان تست با pytest:

from wallet import DigitalWallet

def test_deposit_increases_balance():
    wallet = DigitalWallet("علی", 50.0)
    wallet.deposit(30.0)
    assert wallet.balance == 80.0

تفاوت واضح است. pytest این ایده را به پایتون آورد که تست‌ها باید توابع ساده پایتون باشند، نه متدهای درون کلاس‌های بزرگ. نتیجه‌اش کدی است که خواندنش راحت‌تر و نوشتنش سریع‌تر است.

مشکل اصلی unittest چیست؟

برخی توسعه‌دهندگان unittest را بیش از حد پرحرف می‌دانند. برای نوشتن هر تست باید یک کلاس بسازی، از unittest.TestCase ارث‌بری کنی و از متدهایی مثل assertEqual، assertTrue یا assertRaises استفاده کنی. حفظ کردن این متدها وقت می‌برد و اشتباه در نام‌گذاری آن‌ها هیچ خطایی نمی‌دهد؛ تست فقط نادیده گرفته می‌شود. pytest این پیچیدگی را برداشته. یک کلمه assert کافی است. هر مقایسه‌ای که بخواهی انجام دهی، همین یک کلمه کارت را راه می‌اندازد.

وقتی تست شکست می‌خورد

یکی از جاهایی که تفاوت واقعاً مشخص می‌شود، پیام خطاست. با unittest وقتی یک تست شکست می‌خورد، پیامی شبیه به این می‌بینی:

AssertionError: 100.0 != 80.0

با pytest همان خطا این‌طور نمایش داده می‌شود:

assert wallet.balance == 80.0
  where 100.0 = wallet.balance

pytest وقتی یک ادعا شکست می‌خورد، دقیقاً نشان می‌دهد مقدار واقعی چه بوده و مقدار انتظاری چه بوده، که پیدا کردن دلیل خطا را سریع‌تر می‌کند. این ویژگی در پروژه‌های بزرگ ساعت‌ها وقت صرفه‌جویی می‌کند.

pytest می‌تواند تست‌های unittest را هم اجرا کند

یک نکته مهم که خیلی‌ها نمی‌دانند: unittest در پروژه‌های قدیمی هنوز به طور گسترده استفاده می‌شود. اگر روزی وارد یک پروژه شدی که با unittest نوشته شده، نگران نباش. pytest می‌تواند تست‌های unittest را بدون هیچ تغییری اجرا کند. این یعنی مهاجرت از unittest به pytest تدریجی است و نیازی به بازنویسی یکباره همه چیز نیست.

پس چرا pytest؟

pytest در رتبه‌بندی فریم‌ورک‌های تست پایتون در جایگاه اول قرار دارد، در حالی که unittest در رتبه پنجم است. این اختلاف از یک دلیل ساده می‌آید: pytest کمتر از توسعه‌دهنده می‌خواهد و بیشتر به او می‌دهد. کد کمتر، خطای واضح‌تر، قابلیت‌های بیشتر.

در تمام این دوره با pytest کار می‌کنیم. اما آشنایی با unittest هم ارزش دارد؛ چون در دنیای واقعی با هر دو روبرو خواهی شد.

جایگاه تست‌نویسی در چرخه توسعه نرم‌افزار

۱۹ جولای ۲۰۲۴. یک به‌روزرسانی نرم‌افزاری از شرکت CrowdStrike منتشر شد. در عرض چند ساعت، ۸.۵ میلیون سیستم ویندوزی در سراسر جهان از کار افتادند. خطوط هوایی، بیمارستان‌ها، بانک‌ها و اورژانس‌ها تعطیل شدند. خسارت مالی این حادثه حداقل ۱۰ میلیارد دلار برآورد شده و به عنوان بزرگ‌ترین قطعی IT در تاریخ ثبت شد. دلیل اصلی چه بود؟ یک باگ در سیستم کنترل کیفیت CrowdStrike و فرآیند ناکافی تست. این فقط یک مثال نیست. این یک درس است.

چرخه توسعه نرم‌افزار چیست؟

هر نرم‌افزاری از یک نقطه شروع می‌کند و مراحل مشخصی را طی می‌کند تا به دست کاربر برسد. به این مسیر، چرخه توسعه نرم‌افزار یا SDLC می‌گویند. این چرخه معمولاً هفت مرحله دارد: برنامه‌ریزی، تحلیل نیازها، طراحی، پیاده‌سازی، تست، استقرار و نگهداری. در نگاه اول به نظر می‌رسد تست فقط در مرحله پنجم جایگاه دارد. اما این تصور اشتباه است.

تست فقط یک مرحله نیست

تست به طور رسمی مرحله پنجم است، اما رویکردهای مدرن توسعه نرم‌افزار بررسی کیفیت را در تمام هفت مرحله ادغام می‌کنند. یک مثال ساده: فرض کن در مرحله طراحی، تصمیم اشتباهی درباره ساختار پایگاه داده گرفته می‌شود. اگر کسی همان لحظه این مشکل را نبیند، کد روی آن ساختار اشتباه نوشته می‌شود. وقتی در مرحله تست متوجه مشکل می‌شوند، باید هم طراحی را عوض کنند، هم کد را از نو بنویسند. هرچه زودتر مشکل پیدا شود، رفع آن ساده‌تر است. این یک قانون ساده اما مهم است.

مفهوم Shift-Left چیست؟

رویکرد Shift-Left یعنی فعالیت‌های تست را تا حد ممکن به ابتدای چرخه توسعه منتقل کنیم، حتی از مرحله تحلیل نیازها و طراحی. اگر چرخه توسعه را مثل یک خط زمانی از چپ به راست تصور کنی، Shift-Left یعنی تست را به سمت چپ این خط بکشیم. یعنی زودتر شروع کنیم. توسعه‌دهندگان پایتون که با pytest کار می‌کنند، این اصل را هر روز اجرا می‌کنند. هر بار که کدی می‌نویسند، بلافاصله تست آن را هم می‌نویسند. منتظر مرحله پنجم نمی‌مانند.

در عمل کِی تست بنویسیم؟

سه رویکرد اصلی در دنیای توسعه نرم‌افزار وجود دارد:

بعد از نوشتن کد: رایج‌ترین روش در بین تازه‌کارها. کد نوشته می‌شود، بعد تست برایش تهیه می‌شود. مشکلش اینجاست که گاهی کد به گونه‌ای نوشته می‌شود که تست کردنش سخت است.

همزمان با نوشتن کد: رویکرد تیم‌های حرفه‌ای. کد و تست با هم رشد می‌کنند. هر تابع جدید، یک تست جدید هم دارد. در این دوره بیشتر از این روش استفاده می‌کنیم.

قبل از نوشتن کد (TDD): روشی که در درس بعد بیشتر درباره‌اش صحبت می‌کنیم. اول تست نوشته می‌شود، بعد کدی که آن تست را پاس کند.

پروژه کیف پول دیجیتال در این چرخه کجاست؟

در این دوره یک سیستم مالی می‌سازیم. متدهایی مثل deposit، withdraw و transfer هر کدام رفتارهای مشخصی دارند که اگر اشتباه پیاده‌سازی شوند، پول کاربران در خطر است. تست‌نویسی با pytest به ما اجازه می‌دهد از همان لحظه‌ای که اولین خط کد را می‌نویسیم، یک سیستم نظارتی داشته باشیم. نه وقتی کار تمام شد، نه وقتی کاربر با مشکل روبرو شد. همین الان.

معرفی TDD به زبان ساده

یک سوال عجیب: آیا می‌شود قبل از اینکه کدی بنویسی، تست آن را بنویسید؟
منطقی به نظر نمی‌رسد. چطور می‌توانی چیزی را که هنوز وجود ندارد تست کنید؟ اما دقیقاً همین ایده، یکی از مهم‌ترین روش‌های توسعه نرم‌افزار در دنیاست.

TDD چیست؟

توسعه آزمون‌محور یا Test-Driven Development یک رویکرد در توسعه نرم‌افزار است که در آن تست‌ها قبل از کد اصلی نوشته می‌شوند. توسعه‌دهنده فقط به اندازه‌ای کد می‌نویسد که تست پاس شود، بعد هر دو را بهبود می‌دهد. 
Kent Beck این روش را در اواخر دهه ۱۹۹۰ به عنوان بخشی از برنامه‌نویسی فشرده (Extreme Programming) معرفی کرد. کتاب او با نام «Test-Driven Development: By Example» هنوز هم یکی از مراجع اصلی این حوزه است.

چرخه Red-Green-Refactor

قلب TDD یک چرخه سه‌مرحله‌ای است که توسعه‌دهندگان آن را با رنگ‌ها می‌شناسند.

قرمز (Red): یک تست برای رفتاری می‌نویسی که هنوز وجود ندارد. این تست باید شکست بخورد؛ چون کدی که آن را پاس کند هنوز نوشته نشده.
سبز (Green): کمترین مقدار کدی که تست را پاس کند می‌نویسی. هدف اینجا فقط سبز شدن تست است، نه نوشتن کد زیبا. 
بازنویسی (Refactor): کد را تمیز می‌کنی و بهبود می‌دهی؛ بدون اینکه رفتارش تغییر کند. تست همچنان سبز می‌ماند و تضمین می‌کند که چیزی خراب نشده.
این سه مرحله را بارها تکرار می‌کنی. هر تکرار چند دقیقه طول می‌کشد.

یک مثال ملموس با کیف پول دیجیتال

بگذار این چرخه را روی پروژه خودمان ببینیم. مرحله قرمز: قبل از اینکه متد deposit را بنویسیم، تستش را می‌نویسیم:

def test_deposit_increases_balance():
    wallet = DigitalWallet("علی", 50.0)
    wallet.deposit(30.0)
    assert wallet.balance == 80.0

این تست را اجرا می‌کنیم. قرمز می‌شود؛ چون کلاس DigitalWallet هنوز وجود ندارد.
مرحله سبز: حالا کمترین کدی که این تست را پاس کند می‌نویسیم:

class DigitalWallet:
    def __init__(self, owner, balance):
        self.owner = owner
        self.balance = balance

    def deposit(self, amount):
        self.balance += amount

تست را اجرا می‌کنیم. سبز می‌شود.
مرحله بازنویسی: حالا کد را بهتر می‌کنیم. مثلاً یک اعتبارسنجی برای جلوگیری از موجودی منفی اضافه می‌کنیم. تست را دوباره اجرا می‌کنیم تا مطمئن شویم همچنان سبز است.

TDD چه مزیتی دارد؟

یک نتیجه غیرمنتظره TDD اینجا خودش را نشان می‌دهد: وقتی قبل از کد، تست می‌نویسید، مجبور می‌شوید از ابتدا فکر کنید که این تابع دقیقاً چه کاری باید انجام دهد. این فکر کردن، طراحی کد را بهتر می‌کند. TDD توسعه‌دهندگان را مجبور می‌کند کُندتر پیش بروند، کد را در چرخه‌های کوتاه‌تر اعتبارسنجی کنند و مدل ذهنی قوی‌تری از کدی که می‌نویسند داشته باشند.

آیا در این دوره از TDD استفاده می‌کنیم؟

نه به صورت سختگیرانه. این دوره برای مقدماتی طراحی شده و TDD یک مهارت است که با تمرین یاد گرفته می‌شود. اما در برخی درس‌ها، عمداً اول تست می‌نویسیم و بعد کد. این کار به تو کمک می‌کند که ذهنیت TDD را لمس کنید، حتی اگر همیشه از آن پیروی نکنید. دانستن این روش، حتی اگر همه وقت‌ها استفاده نشود، نگاه توسعه‌دهنده به کد را عوض می‌کند.

جایگاه تست‌نویسی در بازار کار پایتون

یک آگهی استخدامی را تصور کنید. عنوانش «توسعه‌دهنده پایتون» است. در بخش مهارت‌های مورد نیاز، بعد از Django و FastAPI، یک خط کوتاه نوشته: «آشنایی با pytest و تست‌نویسی». این دیگر یک مهارت فرعی نیست. تبدیل به یک الزام شده.

پایتون کجا ایستاده؟

قبل از اینکه از تست‌نویسی حرف بزنیم، باید ببینیم پایتون در بازار کار امروز چه جایگاهی دارد. طبق داده‌های Stack Overflow در سال ۲۰۲۵، پایتون را ۵۷.۹٪ از توسعه‌دهندگان در کارشان استفاده می‌کنند. این بزرگ‌ترین رشد سالانه‌ای بود که هر زبان برنامه‌نویسی بزرگی تجربه کرده. تقاضا برای مهارت پایتون در آمریکا در ژوئن ۲۰۲۵ نسبت به سال قبل ۱۵۳٪ رشد داشته. این عدد بزرگی است.

حالا سوال اینجاست: وقتی این همه برنامه‌نویس پایتون در بازار هستند، چه چیزی یکی را از دیگری متمایز می‌کند؟

تست‌نویسی؛ مرز بین جونیور و سنیور

شرکت‌ها هنگام استخدام توسعه‌دهنده پایتون، تسلط به فریم‌ورک‌های تست مثل pytest را به عنوان یکی از مهارت‌های اصلی مورد نیاز ذکر می‌کنند. دلیلش ساده است. یک توسعه‌دهنده که تست می‌نویسد، کدهایش قابل اعتمادتر است. تغییرات آرام‌تر خراب می‌شوند. کار تیمی راحت‌تر پیش می‌رود. از نظر یک مدیر فنی، این تفاوت مستقیم روی سرعت و هزینه پروژه اثر می‌گذارد. در مصاحبه‌های فنی شرکت‌های بزرگ، نوشتن تست برای کدی که روی صفحه می‌نویسی یک انتظار معمول است. نه یک سوال سخت. یک انتظار پایه.

اعداد واقعی از بازار کار

در بریتانیا، حقوق میانه مشاغلی که pytest را در آگهی استخدامی ذکر کرده‌اند در سال ۲۰۲۵ به ۸۰,۰۰۰ پوند در سال رسیده، که نسبت به سال قبل ۲۳٪ رشد داشته است. در آمریکا، میانگین حقوق سالانه یک Python Tester حدود ۱۴۰,۰۰۰ دلار است. این رقم از میانگین حقوق یک توسعه‌دهنده عمومی پایتون بالاتر است. این اعداد نشان می‌دهند که تخصص در تست نرم‌افزار با پایتون، مستقیماً روی درآمد اثر می‌گذارد.

pytest در پروژه‌های واقعی

شرکت‌هایی که از pytest استفاده می‌کنند فقط استارتاپ‌های کوچک نیستند. Mozilla، Dropbox، Instagram و بسیاری از شرکت‌های بزرگ دیگر pytest را در فرآیند توسعه خود دارند. وقتی به یک پروژه متن‌باز پایتون در GitHub نگاه می‌کنید، تقریباً در همه آن‌ها یک پوشه tests/ می‌بینی و فایل‌هایی که با test_ شروع می‌شوند. این دیگر یک انتخاب نیست؛ استانداردی است که جامعه پایتون پذیرفته است.

سخن پایانی

طبق اداره آمار کار آمریکا، مشاغل توسعه نرم‌افزار بین سال‌های ۲۰۲۴ تا ۲۰۳۴ رشد ۱۵ درصدی خواهند داشت. این یعنی بازار کار توسعه‌دهندگان پایتون در سال‌های آینده بزرگ‌تر می‌شود.

اما بزرگ‌تر شدن بازار، رقابت را هم بیشتر می‌کند. تست‌نویسی یکی از مهارت‌هایی است که این رقابت را به نفع توسعه‌دهنده‌ای که بلد است تغییر می‌دهد.

در پایان این دوره، یک مهارت عملی دارید که می‌توانید آن را در پروژه‌های واقعی نشان دهید. نه فقط در رزومه، بلکه در کدی که می‌نویسید.