Django way s2, Database Mocking ep1

https://realpython.com/django-pytest-fixtures/ عکس از
https://realpython.com/django-pytest-fixtures/ عکس از


خب یه بخش دیگه از روش های بهتر کار کردن با جنگو رو قصد دارم در اینجا با شما بررسی کنیم قبل از هرچیزی یه توضیح در مورد اسم این نمونه پست ها بدم شماره اول که بعد از s نوشته شده شماره پست هست شماره ای که بعد از ep نوشته شده تعداد پستایی که تا الان در مورد یه مبحث خواص پست گزاشتم

میتونید از اینجا : Django way (روشهای بهتر کار کردن با جنگو) پست قبلی رو بخونید

این مبحث هم به احتمال زیاد در چند بخش ادامه پیدا کنه

اول از همه طرح مسيله :

امروزه برنامه ها رشد خیلی زیادی کردن و در تولید یک برنامه باید سرعت در توسعه و stable بودن کد ها رو کنار هم پیاد کرد برای همین متدولوژی های مختلفی برای پیش بردن یک پروژه ارايه شده که در اکثر اونها تست کردن کد ها و coverage گرفتن از اونها حرف اول رو میزنه مسلما جنگو هم از این قضیه هم نقش خودش رو ایفا کرده و روش هایی برای آسون کردن تست نویسی درست کرده

من توی این پست از rest framework استفاده میکنم

خب بریم ببینیم که چطور یه تست کوچیک میشه برای api خودمون بنویسیم:

به فرض داریم :

# ./product/models.py
from django.db import models
class Product(models.model):
   name = models.CharField(max_length=250)
   description = models.TextField(max_length=250, blank=True, null=True)
   image = models.ImageField(blank=True, null=True)
   price = models.FloatField()

# ./product/api/serializer.py
from rest_framework import serializers
from product.models import Product

class ProductSerializer(serializers.ModelSerializer):
     class Meta:
        model = Prodcut
        fields = '__all__'


# ./product/api/views.py
from .serializer import ProductSerializer
from from product.models import Product
from rest_framework import viewsets

class ProductViewSet(viewsets.ModelViewSet):
      serializer_class = ProductSerializer
      query_set = Product.objects.all()

# ./prodcut/urls.py
from rest_framework.routers import SimpleRouter
from .api import views as api_views

urlpatterns = [
]

product_router = SimpleRouter()
product_router.register('product', api_views.ProductViewSet, basename='product')
api_patterns = product_router.urls

خب حالا برنامه ما حاضره و در حالت عادی میخوایم تستش کنیم مسلما تستمون یه چیزی شبیه این میشه :

#./prodcut/tests.py
from rest_framework.test import APITestCase

class ProductTestView(APITestCase):
      @classmethod
      def setUpTestData(cls): 
         cls.product_1 = Product.objects.create(
            name=&quotXbox one&quot,
            description=&quoti need xbox one please&quot,
            price=800000000,
       )
      cls.product_2 = Product.objects.create(
            name=&quotPS4&quot,
           description=&quoti need Ps4 right now&quot,
            price=3100000000,
      ) 
      cls.product_3 = Product.objects.create( 
            name=&quotneed for speed&quot, 
            description=&quotneed for speed most wanted&quot,
            price=5000
        )
       # and....
    
    def test_get_product_list(self):
         response = self.client.get(self.url)
         assert response.data['result'][0]['name']==&quotXbox one&quot
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.data['count'],10)
         

خوب فکر کنم تا اینجاش فهمیدید چه زخمی خوردیم تازه برنامه ما کوچیک هست که تونستیم به همین مقدار کد تمومش کنیم( تازه عکسم من به دیتام اضافه نکردم چون واقعا دردسره) توی دنیای واقعی با دیتا های واقعی هیچ وقت همه چیز به سادگی دو تا فیلد توی مدلامون نیست...

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

راه حل:

در این مواقع که ما نیاز به دیتای فیک داریم از تکنیک Database Mocking استفاده میکنیم

به نقل از این منبع : https://wiki.genexus.com/

تکنیک Database Mocking روشی برای شبیه سازی دیتابیس اصلی به همراه تعدادی رکورد برای انجام تست های مورد نیاز است

یکی از راحت ترین روش ها در پایتون برای Database Mocking استفاده از ماژول factory_boy و faker هست که کار را فوق العاده راحت میکنن همچنین ماژول factory_boy مدل های جنگو رو به صورت درونی ساپورت میکنه و توی داکیومنتش هم مثال های خوبی داره که راحت میتونی باهاش کارتون رو راه بندازید ماژول faker هم برای ساختن دیتا هم فیک و الکی از lorem_ipsum تا رمز و پسور فیک و حتی اسم شهر و کشور رو میشه باهاش تولید کرد البته factory_boy به صورت درونی ماژول faker رو هم ساپورت میکنه و نیازی نیست که به صورت مستقیم از faker استفاده بشه.

( یه فکت جالب در مورد ماژول factory_boy اینه که موقع ساخت این ماژول از تکنیک های یه ماژول قدیمی تر به توی زبان روبی به اسم factory_girl استفاده شده :)))) )

ماژول factory_boy چطور کار میکنه :

برای کار با این ماژول بهتره که توی پوشه اپمون یه فایل به اسم factories.py بسازیم و کدمون رو اینجوری بنویسیم:

#./product/factories.py

import factory
from factory import django
from .models import Product

class ProductFactory(django.DjangoModelFactory):
        name = factory.Faker('name')
        image = django.ImageField(color='blue') 
        description =  factory.Faker(&quottext&quot, max_nb_chars=200)
        price = factory.Faker(&quotrandom_int&quot, min=1000, max=9999999)

        class Meta:
                 model = Product

خب حالا برای ساخت دیتا توی تستمون این کارو میکنیم :

#./prodcut/tests.py
from rest_framework.test import APITestCase
from .factories import ProductFactory

class ProductTestView(APITestCase):
      @classmethod
      def setUpTestData(cls): 
          cls.prodcuts = ProductFactory.create_batch(10)             
    
       def test_get_product_list(self):
             response = self.client.get(self.url)
             assert response.data['result'][0]['name'] == self.product[0].name
             self.assertEqual(response.status_code, 200)
             self.assertEqual(response.data['count'],10)
         

همون طور که دیدید همه چیز راحت و زیبا و بسیار تمیز شد همه چیز سر جای خودشه و موقع عوض شدن api به خاطر به هم ریختن تست هامون گریه نمیکنیم :))


این اولین بخش از قسمتای کار با ماژول factory boy بود چند قسمت دیگه هم در موردشون حرف خواهم زد اگه دوست داشتید میتونید منو فالو کنید تا بقیه قسمت ها رو راحت پیدا کنید