OOZOU
تواصلوا معنا
العودة إلى المدونة

لا تتجاوزوا أي callback في اختباراتكم

قصة callback واحد فاتني في Rails، وكلّفني يومًا كاملًا حتى عثرت عليه.

10 فبراير 2015
5 دقائق للقراءة
لا تتجاوزوا أي callback في اختباراتكم

في كود SlimWiki، استغرق مني اختبار واحد فاشل يومًا كاملًا حتى عرفت سببه. والأسوأ أن الـ spec ينجح حين يُشغَّل المثال وحده، لكنه يفشل حين يُشغَّل ضمن مجموعة الاختبارات كاملة. هذه قصة callback واحد فاتني في Rails، وكلّفني يومًا كاملًا حتى عثرت عليه.

rspec spec/features/some_spec.rb:123   # passes
rspec spec/features/some_spec.rb       # fails, wtf!

عندما بحثت في الأمر، تبيّن أن factory girl لدينا يُنتج حسابًا غير مدفوع حين نطلب منه حسابًا مدفوعًا.

pry> FactoryGirl.create(:paid_account).subscription.paid?
=> false

The :paid_account factory declares a :pro_subscription like this:

factory :paid_account, :parent => :account do association :subscription, :factory => :pro_subscription # ... end

But that factory works fine:

pry> FactoryGirl.create(:pro_subscription).paid? => true

After one day and sprinkling a bit of binding.pry here and there…

factory :paid_account, :parent => :account do

after(:build) do |account| binding.pry end end

It turned out that after building the :paid_account (but before saving), the subscription is paid, which is correct.

pry> account.subscription.paid? => true

After the account is saved, however, it turned into a non-paid account!

pry> account.save! => true pry> account.subscription.paid? => false # wtf!!

لا بد أن شيئًا ما يغيّر الحالة العامة (global state) للنظام، فما هو يا ترى؟ أحد الأسباب المحتملة هو تعديل الأصناف (classes) مباشرة لتجاوز callbacks مؤقتًا. لذلك بحثت بـ grep عن skip_callback في ملفات الـ specs، فوجدت هذا الـ factory الذي يبدو للوهلة الأولى بلا علاقة بالمشكلة:

factory :account_without_subscription, :parent => :account do
  to_create do |account|
    account.class.skip_callback :create, :after, :create_subscription
    account.save!
    account.class.set_callback :create, :after, :create_subscription
  end
end

نحتاج أحيانًا إلى إنشاء حساب بلا اشتراك. ولتحقيق ذلك نتجاوز الـ callback مؤقتًا قبل إنشاء الحساب، ثم نعيده إلى الصنف بعد إنشاء الحساب.

يبدو الأمر سليمًا، أليس كذلك؟ انظروا الآن إلى ملف Account.rb لدينا:

after_create :create_subscription, :if => 'subscription.blank?'

لاحظوا شرط :if هنا، وغيابه في الـ factory أعلاه. هذا الفرق يجعل الاشتراك يُستبدل باشتراك غير مدفوع دون أي شرط. ويمتد أثر ذلك طوال تشغيل rspec، فيفشل كل اختبار لاحق يعتمد على اشتراك مدفوع.

تعلّمت اليوم أن تجاوز callbacks، أو تعديل سلوك أي صنف أثناء التشغيل عمومًا، أمر خطير ومعرّض للأخطاء.

البديل هو تعريف accessor:

# Attributes (for tests)
attr_accessor :_skip_creating_subscription

# ...
after_create :create_subscription, :if => 'subscription.blank?',
                               :unless => :_skip_creating_subscription

الشرطة السفلية في بداية اسم الخاصية تعني أنها مخصصة للاختبارات، وليست للاستخدام العام. وأصبح الـ factory لدينا أبسط بكثير أيضًا:

factory :account_without_subscription, :parent => :account do
  _skip_creating_subscription { true }
end

أهمية callbacks في Rails

الـ callbacks في Rails أدوات قوية تتيح لكم تشغيل methods محددة في مراحل معيّنة من دورة حياة الكائن. وهي ضرورية للحفاظ على سلامة البيانات وأتمتة المهام، مثل إرسال بريد إلكتروني بعد تسجيل المستخدم، أو إنشاء سجلات مرتبطة بعد حفظ السجل الرئيسي. وفي سياق FactoryBot، قد يكون فهم الاستخدام الصحيح لـ callbacks هو الفرق بين مجموعة اختبارات متينة وأخرى كثيرة الأخطاء.

مخاطر تجاوز callbacks

من المغري تجاوز callbacks لتسهيل الاختبار، لكن لذلك عواقب غير مقصودة. فتجاوزها قد يُحدث تناقضات في بيئة الاختبار، ويؤدي إلى نتائج إيجابية أو سلبية كاذبة. فإذا كان الـ callback يضبط قيمة افتراضية أو يفرض قاعدة عمل، فإن تجاوزه ينتج بيانات في حالة غير صالحة لا تعكس بيئة الإنتاج.

بدائل تجاوز callbacks

بدلًا من تجاوز callbacks، فكّروا في استخدام منطق شرطي يتحكم في تنفيذها. أي أن تضيفوا شروطًا إلى الـ callbacks بحيث لا تُتجاوز إلا عند الحاجة. فاستخدام خاصية مثل _skip_creating_subscription مثلًا طريقة أنظف وأكثر أمانًا لإدارة تنفيذ callbacks دون المساس بالحالة العامة للتطبيق.

FactoryBot لاختبارات أكثر موثوقية

FactoryBot أداة ممتازة لتجهيز بيانات الاختبار، لكن جودتها مرهونة بجودة إعدادها. احرصوا على أن تكون الـ factories معرّفة جيدًا، وأن تُستخدم callbacks بحكمة. وهذا يعني التفكير جيدًا في متى تستخدمون after_create ومتى تستخدمون after_build، والتأكد من أن أي منطق شرطي موثّق ومفهوم لدى فريقكم.

callbacks في الاختبارات

لتحافظوا على مجموعة اختبارات سليمة، اتبعوا ما يلي:

  • وثّقوا الغرض من كل callback وأثره في النظام.
  • استخدموا الشروط للتحكم في callbacks بدلًا من تجاوزها.
  • راجعوا الـ factories وأعيدوا هيكلتها بانتظام لتتوافق مع قواعد العمل.
  • لا تعدّلوا الحالة العامة في الاختبارات، تجنبًا لآثار جانبية على الاختبارات الأخرى.

باتباع هذه النصائح ستتمكنون من استخدام callbacks دون الإضرار بمجموعة اختباراتكم.

والآن عدنا إلى تشغيلنا الرائع!

Related Articles

كيف أصمّم برمجيات؟ دليل المبتدئين لأصحاب الأعمال

كيف أصمّم برمجيات؟ دليل المبتدئين لأصحاب الأعمال

تصميم البرمجيات عملية ممتعة لكنها معقدة، تتطلب الإبداع والتفكير المنطقي ومهارات حل المشكلات.

اقرأوا المزيد
مستقبل الحوسبة الطرفية: ما تحتاج الشركات إلى معرفته

مستقبل الحوسبة الطرفية: ما تحتاج الشركات إلى معرفته

يتطور المشهد الرقمي بسرعة، وعلى الشركات أن تتكيّف لتواكب الطلب المتزايد على السرعة والكفاءة والمعالجة في الوقت الفعلي.

اقرأوا المزيد
مستقبل قطاع التجزئة: الذكاء الاصطناعي والواقع المعزّز واتجاهات التحوّل الرقمي

مستقبل قطاع التجزئة: الذكاء الاصطناعي والواقع المعزّز واتجاهات التحوّل الرقمي

يتطور قطاع التجزئة بوتيرة غير مسبوقة بفضل الذكاء الاصطناعي (AI) والواقع المعزّز (AR) وتقنيات التحوّل الرقمي. فلم تعد المتاجر التقليدية مجرد أماكن لبيع المنتجات، بل أصبحت متاجر ذكية وتفاعلية.

اقرأوا المزيد

Have a Project in Mind?

Let's discuss how we can help bring your ideas to life with our expertise in web and mobile development.

ابدأوا المحادثةhello@oozou.com
OOZOU Logo

بانكوك · سنغافورة · هونغ كونغ

X
Facebook
Instagram
LinkedIn
OOZOU Co., Ltd.
548 One City Centre
Ploenchit Road
Bangkok 10330
Thailand
+66 2544 0000
hello@oozou.com

الخدمات

  • تطوير الويب
  • وكلاء الذكاء الاصطناعي والذكاء الاصطناعي التوليدي
  • تطوير تطبيقات الجوال
  • تحليلات البيانات وهندستها
  • تصميم تجربة المستخدم وواجهته (UX/UI) وتصميم المنتجات
  • التحوّل الرقمي

الشركة

  • من نحن
  • الوظائف
  • المدونة
  • دراسات الحالة
  • عروض حية
  • تعلّمت اليوم
  • تواصل معنا

المزيد

  • القطاعات
  • شبكة الشركاء
© 2026 OOZOU. جميع الحقوق محفوظة.
سياسة الخصوصيةمدونة قواعد السلوكسياسة مكافحة الرشوة والفساد