یه تابستون متفاوت با یه تصمیم هوشمندانه! دوره هوش مصنوعی یه تابستون متفاوت با یه تصمیم هوشمندانه! دوره هوش مصنوعی
🎯 ثبت نام
بستن تبلیغات
دوره تسلط بر پایتون ؛ آموزش پروژه محور برای حرفه ای ها

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

مشاهده بیشتر
دوره آموزش ماشین لرنینگ

اگه پایتون بلدی و می‌خوای وارد دنیای هوش مصنوعی بشی، این دوره مخصوص توئه! با آموزش پروژه‌محور و همراهی اساتید حرفه‌ای، یاد بگیر چطور از هوش مصنوعی تو زمینه‌هایی مثل پزشکی، بورس و املاک استفاده کنی.

مشاهده بیشتر
یادگیری عمیق از پایه تا پیشرفته، همراه با پروژه‌های واقعی!

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

مشاهده بیشتر
دوره پروژه محور آموزش جنگو

با این دوره، Django رو از پایه شروع کن و به یک حرفه‌ای تبدیل شو! یاد بگیر چطور با معماری MVT، پایگاه داده و RESTful API کار کنی، پروژه‌های واقعی بسازی و آن‌ها رو روی وب سرور مستقر کنی!

مشاهده بیشتر

تست یک وب اپلیکیشن در Django

آموزش جنگو

تست یک اپلیکیشن وب در Django


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


یکی از راه های کاهش این مشکلات، ایجاد تست های اتوماتیک است که پس از هر تغییر در برنامه به راحتی و درستی اجرا میشوند. در این بخش آموزش، روش اتوماتیک سازی unit testing در وب سایت تان، با استفاده از Django test framework، را به شما می آموزیم.


پیش نیاز یادگیری مقاله تست یک اپلیکیشن وب در جنگو


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


اهداف :


فهم روش نوشتن unit test های اتوماتیک برای وب سایت های Django-based


بررسی کلی


وب سایت LocalLibrary در حال حاضر دارای صفحاتی برای نمایش تمام کتاب ها و نویسندگان، صفحات detail view برای item های Book و Author ، صفحاتی برای تمدید BookInstance ها و صفحات ایجاد، به روز رسانی و حذف item های Author ( و Book، اگر چالش بخش فرم ها را با موفقیت انجام داده باشید) می باشد.


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


تست های اتوماتیک میتوانند این مشکل را به راحتی برای ما حل کنند! سرعت تست بسیار بالا، تست جزئیاتی با دقت بسیار بیشتر، تست دقیق یک عملکرد با هر بار اجرا ( با اعتبار بسیار بیشتر از تست های دستی! ) از مزایای بارز آنهاست. به علت سرعت بالای اجرا، تست های اتوماتیک را میتوان به دفعات بیشتری اجرا کرده و در صورت شکست، از مکان دقیق بروز مشکل مطلع شد.


تست های اتوماتیک میتوانند به عنوان اولین "کاربر" واقعی کد شما نیز رفتار کرده و شما را وادار به دقت فراوان در تعریف و مستند سازی رفتار وب سایت تان کنند. معمولا این تست ها مبنای مثال ها و مستند سازی های کد شما هستند. به همین دلیل برخی از فرآیند های تولید نرم افزار با تعریف و پیاده سازی تست ها آغاز شده و سپس کدی که عملکرد مناسب تست را داشته باشد نوشته میشود ( برنامه نویسی test_driven و behavior_driven ).


در این بخش آموزش، با اضافه کردن تعدادی تست به وب سایت LocalLibrary، روش نوشتن تست های اتوماتیک برای Django را به شما آموزش میدهیم.


انواع تست در Django


تست ها در انواع (types)، سطوح ( levels) ، طبقه بندی ( classification) و رویکرد های متفاوتی موجود هستند. مهمترین تست های اتوماتیک به شرح زیر هستند:



Unit test ها


عملکرد اجزا را به شکل تکی، معمولا در سطح تابع و کلاس، بررسی میکنند.



Regression test ها


این تست ها bug های قدیمی را نشان داده و در درجه اول برای اطمینان از رفع bug ها اجرا میشوند. در اجرا های بعدی این تست ها از نبود مشکلی در چالش های جدید ایجاد شده در کد، اطمینان حاصل میکنند.



Integration test ها


این تست ها عملکرد صحیح گروهی از اجزا در زمان کار با یکدیگر را نمایش میدهند. تست های Integration از ارتباطات موجود بین اجزا آگاه هستند اما همیشه به عملکرد های درونی هر جز نمیپردازند و معمولا بر اساس گروه بندی ساده ای از اجزا درون وب سایت عمل میکنند.


توجه:

black box، white box، دستی ، اتوماتیک، canary، smoke، conformance، acceptance، functional، system ، performance، load و stress test نیز از انواع دیگری از تست های متداول هستند.


Django چه امکاناتی برای تست در اختیار ما قرار میدهد؟


تست تمامی اجزا یک وب سایت کار پیچیده ای است زیرا معمولا باید چندین لایه منطقی، از مدیریت درخواست های سطح HTTP و مدل های کوئری تا تایید و پردازش فرم ها و template rendering، مورد بررسی قرار بگیرند.


Django یک test framework با سلسه مراتب کوچکی از کلاس ها که بر اساس کتابخانه Python standard unittest library ساخته شده است برای ما فراهم کرده است.


این test framework، برخلاف نامش، برای انجام تست های unit و integration مناسب است. این Django framework با اضافه کردن ابزار و متد های API در تست کردن عملکرد های وب و مختص Django به ما کمک میکند.


با استفاده از این ابزار میتوانید درخواست های مورد نیاز خود را شبیه سازی کرده و با وارد کردن داده های آزمایشی، خروجی اپلیکیشن خود را مشاهده کنید. Django همچنین API ها (LiveServerTestCase) و ابزار هایی برای استفاده از framework های مختلف تست، در اختیار ما قرار میدهد. مثلا میتوانید با کمک framework Selenium روابط یک کاربر و جستجو گر را شبیه سازی کنید.


برای نوشتن یک تست از یکی از کلاس های test base موجود در Django ( SimpleTestCase, TransactionTestCase, TestCase, LiveServerTestCase ) derive کرده و متد های جداگانه ای برای بررسی عملکرد صحیح هر بخش مینویسیم ( تست ها از متد “assert” برای بررسی True یا False بودن مقادیر، مساوی بودن دو مقدار و ... استفاده میکنند). پس از یک اجرای ازمایشی، framework متد های تست انتخابی را در کلاس های مشتق شده اجرا میکند. متد های تست به شکل مستقل، با setup معمول و یا tear-down تعریف شده در کلاس اجرا میشوند که در شکل زیر نمایش داده شده است:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
             class YourTestClass(TestCase):
def setUp(self):
    # Setup run before every test method.
    pass
 
def tearDown(self):
    # Clean up run after every test method.
    pass
 
def test_something_that_will_pass(self):
    self.assertFalse(False)
 
def test_something_that_will_fail(self):
    self.assertTrue(False)
 
                <button></button>

بهترین base class برای اکثر تست ها django.test.TestCase میباشد. این test class یک دیتابیس جدید، پیش از اجرای تست ها، ایجاد کرده و تمامی توابع تست را در transaction خودشان اجرا میکند. این کلاس دارای یک Client آزمایشی نیز هست که برای شبیه سازی ارتباط کاربر با کد در سطح view استفاده میشود. در بخش های بعدی برunit test ها ، که با استفاده از TestCase موجود در base class ساخته شده، تمرکز خواهیم کرد.


توجه:

کار با کلاس django.test.TestCaseبسیار ساده است اما ممکن است برخی تست ها کمی کند تر سطح انتظار ما اجرا شوند ( همه تست ها نیازی به ایجاد دیتابیس مختص خودشان و یا شبیه سازی ارتباطات view ندارند). پس از آشنایی با عملکرد این کلاس میتوانید برخی از تست های خود را با test class های موجود ساده تر جایگزین کنید.



چه چیزی را باید تست کرد؟


باید تمام جنبه های کد خود ، نه کتابخانه ها و عملکرد های Django و Python، را تست کنید.


برای مثال، مدل Author زیر را در نظر بگیرید. در این مدل نیازی نیست first_name و last_name را بررسی کنید که حتما به شکل CharField در دیتابیس ذخیره شده باشند زیرا این عملکرد توسط Django تعریف شده است ( البته عملا ناچار هستید این عملکرد را در طول development تست کنید) و نیازی به تست date_of_birth برای اطمینان از تاریخ بودن این فیلد نیز ندارید زیرا این عملکرد نیز در Django پیاده سازی شده است.


اما باید متن هایی که برای label ها انتخاب میشوند ( First name، Last name، Date of birth ، Died) و سایز فیلد مشخص شده برای متن ( 100 کاراکتر) را بررسی کنید زیرا اینها بخشی ازطراحی شما هستند و میتوانند در آینده تغییر کنند.


1
2
3
4
5
6
7
8
9
10
11
12
13
class Author(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    date_of_birth = models.DateField(null=True, blank=True)
    date_of_death = models.DateField('Died', null=True, blank=True)
 
    def get_absolute_url(self):
        return reverse('author-detail', args=[str(self.id)])
 
    def __str__(self):
        return '%s, %s' % (self.last_name, self.first_name)
 
                    <button></button>

متد های get_absolute_url() و __str__() نیز بخشی از کد شما هستند پس باید عملکرد آنها بررسی شود. برای get_absolute_url() میتوانید فرض کنید که متد reverse() موجود در Django به درستی پیاده سازی شده و تنها بخش های تعریف شده توسط view مرتبط را تست کنید.


توجه:

اگر خواننده دقیقی باشید حتما توجه کرده اید که لازم است محدوده های برخی مقادیر حساس مانند تاریخ تولد و تاریخ مرگ را نیز بررسی کنیم تا از درستی آنها، مثلا اینکه تاریخ مرگ پس از تاریخ تولد باشد، اطمینان حاصل کنیم. در Django این محدودیت ها به form class هایتان اضافه میشوند ( البته میتوانید برای فیلد های مدل validator تعریف کنید . model validator ها، در صورت فراخوانی توسط متد clean() مدل، تنها در سطح فرم اجرا میشوند. این روش نیاز به یک ModelForm و یا فراخوانی متد clean() مدل به شکل اختصاصی دارد).


با توجه به مطالب گفته شده به تعریف و اجرای تست ها میپردازیم.


نگاهی کلی به ساختار تست در Django


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


Django از ماژول built-in test discovery unittest استفاده میکند. این ماژول تست های موجود در هرفایلی ( با نام مطابق الگو test*.py ) را در دایرکتوری در حال فعالیت می یابد.


اگر فایل خود را به درستی نام گذاری کنید، میتوانید از هر ساختار دلخواهی استفاده کنید. توصیه ما این است که یک ماژول برای کد تست خود ایجاد کرده و فایل های متفاوتی برای مدل ها، view ها، فرم ها و بقیه انواع کد هایی که نیاز به تست دارند، داشته باشید. مثلا:


1
2
3
4
5
6
7
catalog/
  /tests/
    __init__.py
    test_models.py
    test_forms.py
    test_views.py
                    <button></button>

در پروژه LocalLibrary خود، فایل هایی با ساختار بالا ایجاد کنید. فایل __init__.py باید یک فایل خالی باشد ( تا پایتون تشخیص دهد که این دایکتوری یک package است). میتوانید با کپی کردن فایل skeleton test ( /catalog/tests.py) و تغییر نام آن، سه فایل تست خود را ایجاد کنید.


توجه:

فایل /catalog/tests.py skeleton test در زمان ساخت Django skeleton website به شکل اتوماتیک ساخته شده است. میتوانید تمام تست های خود را در آن قرار دهید اما در نهایت، فایل بزرگ و غیر قابل مدیریتی خواهید داشت.


میتوانید فایل skeleton را حذف کنید زیرا به آن نیازی نخواهیم داشت.


/catalog/tests/test_models.py را باز کنید. این فایل باید، به شکل زیر، django.test.TestCase را import کند:


1
2
3
4
from django.test import TestCase
 
# Create your tests here.
                    <button></button>

اغلب برای هر model/view/form که باید بر آنها تست انجام شود یک test class ، با متد های مشخصی که عملکرد خاص آنها را تست میکند، ایجاد میکنیم.


اما گاهی ممکن است به یک کلاس جداگانه برای تست یک use case به خصوص ، با test function های مجزا که جنبه ی خاصی از use case را تست میکند، نیاز داشته باشید ( مثلا یک کلاس برای بررسی صحت اعتبار سنجی یک فیلد که محتوی توابعی برای تست هرخطا ممکن باشد). ساختار همچنان تا حد زیادی به شما بستگی دارد و بهتر است که از ساختار یکسان و ثابتی استفاده کنید.


test class زیر را به انتهای فایل بیفزاید. این کلاس نحوه ساخت یک test case class ، با اشتقاق گیری از TestCase، را نمایش میدهد.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class YourTestClass(TestCase):
    @classmethod
    def setUpTestData(cls):
        print("setUpTestData: Run once to set up non-modified data for all class methods.")
        pass
 
    def setUp(self):
        print("setUp: Run once for every test method to setup clean data.")
        pass
 
    def test_false_is_false(self):
        print("Method: test_false_is_false.")
        self.assertFalse(False)
 
    def test_false_is_true(self):
        print("Method: test_false_is_true.")
        self.assertTrue(False)
 
    def test_one_plus_one_equals_two(self):
        print("Method: test_one_plus_one_equals_two.")
        self.assertEqual(1 + 1, 2)
                    <button></button>

این کلاس دو متد، که میتوانید برای تنظیمات پیش از configuration آنها استفاده کنید، را تعریف میکند ( مثلا، برای ایجاد هر شی و یا مدلی که در تست به آنها نیاز دارید ):


  • setUpTestData() – این متد یک بار در ابتدای اجرای تست، برای تنظیمات سطح کلاس، فراخوانی میشود. برای ساخت اشیایی که در طول هیچ یک از متد های تست، ویرایش و یا تغییر داده نمیشوند از این متد استفاده میکنیم.

  • setUp() – پیش از هر test function و به منظور ایجاد اشیایی که در طول تست تغییر خواهند کرد، فراخوانی میشود ( هر تابع تست، یک نسخه جدید از این اشیا دریافت خواهد کرد).


توجه:

class ها دارای متد tearDown() نیز هستند، که ما از آنها استفاده نکرده ایم. این متد برای تست های دیتابیس استفاده نمیشود (زیرا TestCase base class کار تخریب دیتابیس را برای شما انجام خواهد داد).


پس از آنها تعدای متد های تست وجود دارند که از تابع Assert برای بررسی صحیح ، غلط و یا مساوی بودن شروط استفاده میکنند ( AssertTrue، AssertFalse، AssertEqual ). اگر نتیجه بررسی شروط به شکل خواسته شده نباشد، تست با شکست مواجه شده و در کنسول شما error نمایش میدهد.


AssertTrue، AssertFalse و AssertEqual ، assert های استاندارد موجود در unittest هستند. البته assert های دیگری نیز در framework موجودند . Django-specific assertions نیز برای تست تغییر مسیر ) view (assertRedirects ، تست بررسی استفاده از template ای مشخص) (assertTemplateUsed و ... استفاده میشود.


توجه:

معمولا نباید توابع print() را در تست های خود قرار دهید. دراینجا ما برای نمایش ترتیب فراخوانی setup function ها در کنسول، از آنها استفاده میکنیم.


روش اجرای تست ها در Django


ساده ترین روش اجرای تست ها، استفاده از دستور زیر است:


1
2
3
python3 manage.py test
 
                    <button></button>

این دستور تمامی فایل های با الگو نام test*.py را در دایرکتوری در حال فعالیت یافته و تمامی تست های تعریف شده را با استفاده از base class مناسب اجرا میکند (ما فایل های تست زیادی داریم اما در حال حاضر تنها /catalog/tests/test_models.py دارای تست است). به شکل پیش فرض تست ها تنها وجود خطا را گزارش داده و خلاصه ای از آن ارائه میدهند.


توجه:

اگر error هایی مشابه : ValueError: Missing staticfiles manifest entry ... دریافت کردید، ممکن است علت آن عدم اجرای پیش فرض تست به شکل collectstatic باشد که برای حافظه کلاس app شما ضروری است . راه های زیادی برای حل این مشکل وجود دارد، اما ساده ترین آنها اجرای collectstatic پیش از اجرای تست است :


1
2
3
python3 manage.py collectstatic
 
                    <button></button>

تست ها را در دایرکتوری اصلی LocalLibrary اجرا کنید. خروجی باید مشابه خروجی نمایش داده شده زیر باشد:



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
> python3 manage.py test
 
Creating test database for alias 'default'...
setUpTestData: Run once to set up non-modified data for all class methods.
setUp: Run once for every test method to setup clean data.
Method: test_false_is_false.
setUp: Run once for every test method to setup clean data.
Method: test_false_is_true.
setUp: Run once for every test method to setup clean data.
Method: test_one_plus_one_equals_two.
.
======================================================================
FAIL: test_false_is_true (catalog.tests.tests_models.YourTestClass)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\Github\django_tmp\library_w_t_2\locallibrary\catalog\tests\tests_models.py", line 22, in test_false_is_true
    self.assertTrue(False)
AssertionError: False is not true
 
----------------------------------------------------------------------
Ran 3 tests in 0.075s
 
FAILED (failures=1)
Destroying test database for alias 'default'...
                    <button></button>

در اینجا با یک خطا روبرو هستیم و میتونیم مشاهده کنیم که کدام تابع و به چه علت با خطا مواجه شده است ( این خطا کاملا قابل پیش بینی است زیر False با True برابر نیست !).


اين مقاله ادامه دارد


1400/04/07 2660 0
نظرات شما

نظرات خود را ثبت کنید...