---
title: "لا تتجاوزوا أي callback في اختباراتكم"
description: "قصة callback واحد فاتني في Rails، وكلّفني يومًا كاملًا حتى عثرت عليه."
canonical: https://oozou.com/ar/blog/never-skip-a-callback-in-your-tests-21
locale: ar
date: 2015-02-10
author: "OOZOU Team"
---

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

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

Published: 2015-02-10 · Author: OOZOU Team (Engineering) · 5 min read

في كود [SlimWiki](https://slimwiki.com/)، استغرق مني اختبار واحد فاشل يومًا كاملًا حتى عرفت سببه. والأسوأ أن الـ 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](http://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 دون الإضرار بمجموعة اختباراتكم.

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

---

Contact OOZOU: hello@oozou.com · +66 2544 0000 · https://oozou.com/ar/contact

More for AI agents: https://oozou.com/llms.txt · every page has a markdown twin at its URL plus ".md"
