{ اختبار البرمجيات - Software Testing }

أتمتة الفوترة المتكررة: تجنب الفوضى الزمنية في اختبارات الاشتراكات


مقدمة في أتمتة “Automation” الأحداث المتكررة

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

سنستعرض في هذا المقال كيفية تصميم وتنفيذ نظام إدارة اشتراكات فعال باستخدام لغة Python، مع تغطية حالات الحافة واختبار سير عمل الدفع.


تصميم خدمة الاشتراك

لبناء نظام إدارة اشتراكات قوي، يجب تحديد بنية واضحة. جوهر هذا النظام هو فئة Subscription التي تحتوي على جميع التفاصيل ذات الصلة بالاشتراك مثل معرف المستخدم، نوع الخطة، تاريخ البداية، تاريخ الفوترة التالي، الحالة، المبلغ، ومعلومات الإلغاء (إن وجدت). يساعد استخدام Python’s @dataclass في إنشاء هذه الفئة بسهولة مع تقليل الأكواد الزائدة.

@dataclass
class Subscription:
    user_id: str
    plan_type: str
    start_date: datetime
    next_billing_date: datetime
    status: str
    amount: float
    cancelled_at: Optional[datetime] = None

حساب تاريخ الفوترة التالي

واحدة من المهام الرئيسية لنظام إدارة الاشتراكات هي حساب تاريخ الفوترة التالي بناءً على نوع الخطة. يجب أن يأخذ النظام بعين الاعتبار اختلاف عدد الأيام في الأشهر وسنوات الكبيسة.

def _calculate_next_billing_date(self, current_date: datetime, plan_type: str) -> datetime:
    if plan_type == "monthly":
        if current_date.month == 12:
            next_month = 1
            next_year = current_date.year + 1
        else:
            next_month = current_date.month + 1
            next_year = current_date.year
            
        _, last_day = calendar.monthrange(next_year, next_month)
        
        return datetime(
            year=next_year,
            month=next_month,
            day=min(current_date.day, last_day),
            hour=current_date.hour,
            minute=current_date.minute
        )
    elif plan_type == "yearly":
        next_year = current_date.year + 1
        if current_date.month == 2 and current_date.day == 29:
            if not calendar.isleap(next_year):
                return datetime(next_year, 2, 28, current_date.hour, current_date.minute)
        return datetime(next_year, current_date.month, current_date.day, current_date.hour, current_date.minute)
    raise ValueError(f"Unsupported plan type: {plan_type}")

معالجة حالات الحافة

  • نهاية الشهر: يجب أن ينتقل المستخدم المشترك في 31 يناير إلى 28 أو 29 فبراير بشكل صحيح.
  • سنوات الكبيسة: الاشتراكات التي تبدأ في 29 فبراير يجب أن تأخذ في الحسبان السنوات غير الكبيسة.
  • المناطق الزمنية: يجب أن تتماشى تواريخ الفوترة مع المناطق الزمنية المختلفة لتجنب الفروقات بين التواريخ المعروضة والحقيقية.


بناء سير عمل معالجة الدفع

التعامل مع نجاح وفشل الدفع

سير عمل الدفع مسؤول عن التحقق من الاشتراكات النشطة وشحن المستخدمين عند الوصول إلى تاريخ الفوترة التالي.

def process_recurring_payments(self):  
    current_time = self.time_provider.now()  
    for subscription in self.subscriptions.values():  
        if (subscription.status == "active" and  
                subscription.next_billing_date <= current_time):  
            try:  
                self.payment_gateway.charge(subscription.user_id, subscription.amount)  
                subscription.next_billing_date = self._calculate_next_billing_date(  
                    subscription.next_billing_date,  
                    subscription.plan_type  
                )  
            except PaymentError:  
                subscription.status = "payment_failed"

الإلغاء وتأثيره على الفوترة

عند إلغاء الاشتراك، يجب تحديث حالته إلى “ملغي” وتسجيل وقت الإلغاء.

def cancel_subscription(self, user_id: str):  
    if user_id in self.subscriptions:  
        self.subscriptions[user_id].status = "cancelled"  
        self.subscriptions[user_id].cancelled_at = self.time_provider.now()


اختبار الأتمتة – Automation Testing

لضمان موثوقية النظام، يتم اختبار سيناريوهات العالم الواقعي وحالات الحافة باستخدام أدوات مثل unittest.mock لمحاكاة الاعتماديات الخارجية.

سيناريوهات الاختبار

نجاح الفوترة

def test_successful_recurring_payment(subscription_service, mock_payment_gateway, mock_time_provider):  
    subscription = subscription_service.create_subscription("user1", "monthly", 9.99)  
    mock_time_provider.now.return_value = subscription.next_billing_date  
    subscription_service.process_recurring_payments()  
    mock_payment_gateway.charge.assert_called_once_with("user1", 9.99)  
    assert subscription.next_billing_date == datetime(2024, 3, 1, 12, 0)

فشل الدفع

def test_failed_recurring_payment(subscription_service, mock_payment_gateway, mock_time_provider):  
    subscription = subscription_service.create_subscription("user1", "monthly", 9.99)  
    mock_payment_gateway.charge.side_effect = PaymentError()  
    mock_time_provider.now.return_value = subscription.next_billing_date  
    subscription_service.process_recurring_payments()  
    assert subscription.status == "payment_failed"

حالات الحافة

def test_edge_cases_monthly_billing(subscription_service, mock_time_provider):  
    mock_time_provider.now.return_value = datetime(2024, 1, 31, 12, 0)  
    jan_subscription = subscription_service.create_subscription("user1", "monthly", 9.99)  
    assert jan_subscription.next_billing_date == datetime(2024, 2, 29, 12, 0)


الخلاصة

أتمتة الفوترة المتكررة هي وسيلة فعالة لتحسين العمليات وتعزيز تجربة المستخدم. من خلال تنفيذ ممارسات مثل التحقق القوي، الإشعارات الواضحة، والتعامل الفعال مع حالات الفشل، يمكن بناء نظام أتمتة موثوق يقلل الأخطاء ويضمن رضا العملاء.

المصدر: thegreenreport

مقالات ذات صلة

زر الذهاب إلى الأعلى