آزمون ناموفّق

یک آزمون ناموفّق چه شکلی است؟ بیایید get_formatted_name() را تغییر دهیم تا بتواند نام‌های میانی را هم مدیرّیت کند، امّا این کار طوری انجام دهیم که تابع در صورت وارد کردن نام و نام خانوادگی به تنهایی، مانند Janis Joplin، از کار بیفتد.

این نسخه‌ی جدید get_formatted_name() است که نام میانی را هم به عنوان نشان‌دهنده می‌پذیرد:

1
2
3
4
def get_formatted_name(first, middle, last):
    """Generate a neatly formatted full name."""
    full_name = f"{first} {middle} {last}"
    return full_name.title()

این نسخه برای افرادی که نام میانی دارند کار می‌کند امّا هنگام آزمودن آن متوجّه می‌شویم که کارکرد تابع با وارد کردنِ نام افرادی که فقط نام و نام خانوادگی دارند خراب می‌شود. این بار اجرا کردن test_name_function.py این خروجی را می‌دهد:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
E
===================================================================
ERROR: test_first_last_name (__main__.NamesTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_name_function.py", line 8, in test_first_last_name
    formatted_name = get_formatted_name('janis', 'joplin')
TypeError: get_formatted_name() missing 1 required positional argument: 'last'
----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)

اینجا اطّلاعات زیادی وجود دارد زیرا هنگام رد شدن یک آزمون چیزهایی زیادی هست که باید بدانیم. اولّین مورد در خروجی، یک E تنهاست که نشان می‌دهد یک آزمون واحد در این مورد آزمایشی به خطا خورده است. پس از آن می‌بینیم که test_first_last_name() در NamesTestCase موجب خطا شده است. زمانی که آزمایش شما شامل چندین آزمون واحد است، دانستن این که کدام آزمون شکست خورده حیاتی است. یک traceback استاندارد می‌بینیم که می‌گوید فراخوانی تابع get_formatted_name('janis', 'joplin') دیگر کار نمی‌کند زیرا یک نشان‌وندِ مکان‌دارِ الزامی را ندارد. در نهایت می‌بینیم که یک آزمون واحد اجرا شده است. در نهایت پیامی مبنی بر شکست خوردن کلّی مورد آزمایشی می‌بینیم و این که هنگام اجرای آن یک خطا رخ داده است. این اطّلاعات در پایان خروجی پدیدار می‌شود بنابراین آن را بلافاصله می‌بینید بدون این که نیاز داشته باشید کل خروجی طولانی را بپیمایید تا بفهمید چند آزمون شکست خورده است.

پاسخ دادن به یک آزمون ناموفّق

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

در این مورد، get_formatted_name() در ابتدا تنها دو پارامتر داشت: نام و نام خانوادگی. اکنون نام، نام میانی و نام خانوادگی را نیاز دارد. افزودن پارامترِ اجباریِ نامِ میانی، رفتارِ مطلوب get_formatted_name() را مختل کرده است. اینجا بهترین گزینه اختیاری کردن نام میانی است. به محض انجام این کار آزمون ما برای نام‌هایی مانند Janis Joplin هم موفقیّت‌آمیز خواهد بود و می‌توانیم نام‌های میانی را هم بپذیریم. بیایید get_formatted_name() را طوری تغییر دهیم که در آن نام‌های میانی اختیاری باشند و سپس آزمون را دوباره اجرا کنیم. اگر موفّق باشد، در ادامه مطمئن می‌شویم که تابع، نام‌های میانی را هم به خوبی مدیریّت می‌کند.

برای اختیاری کردن نام‌های میانی پارامتر middle را به انتهای فهرست پارامترها منتقل کرده و به آن یک مقدار پیش‌فرض خالی می‌دهیم. همچنین یک آزمون if اضافه می‌کنیم که بسته به وجود داشتن یا نداشتن نام میانی، نام کامل را به درستی ایجاد می‌کند:

1
2
3
4
5
6
7
def get_formatted_name(first, last, middle=''):
    """Generate a neatly formatted full name."""
    if middle:
        full_name = f"{first} {middle} {last}"
    else:
        full_name = f"{first} {last}"
    return full_name.title()

در نسخه‌ی جدید get_formatted_name() نام میانی اختیاری است. اگر نام میانی به تابع داده شود نام کامل شامل نام، نام میانی و نام خانوادگی خواهد بود. در غیر این صورت نام کامل تنها از نام و نام خانوادگی درست خواهد شد. حال تابع باید با هر دو نوع اسم کار کند. برای دریافتنِ این که تابع همچنان با نام‌هایی مانند Janis Joplin کار می‌کند، test_name_function.py را دوباره اجرا کنید:

1
2
3
4
5
6
.

----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

آزمون اکنون موفّق است. این حالت ایده‌آل است زیرا بدون آن که نیاز باشد تابع را به صورت دستی آزمایش کنیم، می‌فهمیم که تابع برای نام‌هایی مانند Janis Joplin هم کار می‌کند. اصلاحِ تابع به این دلیل ساده بود که شکست خوردن در آزمون به ما کمک کرد تا بفهمیم کد جدید رفتار پیشین را مختل کرده است.