سؤال

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

UPDATE objects SET locked = 1 WHERE id = 1234 AND locked = 0

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

UPDATE objects SET results = '...' WHERE id = 1234 AND version = 1

إذا تم تحديث الكائن، فلن يتطابق إصداره وبذلك سيتم تجاهل النتائج.

يجب على هذه التحديثات الذرية التعامل مع أي ظروف السباق المحتملة. والسؤال هو كيفية التحقق من ذلك في اختبارات الوحدة.

إن الإشارة الأولى سهلة الاختبار، حيث إنها ببساطة مسألة إعداد اختبارات مختلفة مع السيناريوهات المحتملة: (1) حيث يتم قفل الكائن و (2) حيث لا يتم تأمين الكائن. (لا نحتاج إلى اختبار ذراع استفسار SQL كما يجب أن تكون مسؤولية بائع قاعدة البيانات.)

كيف اختبار واحد الإشارة الثانية؟ يجب تغيير الكائن من قبل طرف ثالث بعض الوقت بعد الإشارة الأولى ولكن قبل الثانية. سيتطلب ذلك إيقاف مؤقتا في التنفيذ بحيث يتم تنفيذ التحديث بشكل موثوق ويتم تنفيذها باستمرار، لكنني أعرف عدم وجود دعم لنقاط التوقف عن طريق الحقن باستخدام RSPEC. هل هناك طريقة للقيام بذلك؟ أم أن هناك بعض التقنيات الأخرى التي أشر إليها على محاكاة مثل هذه الظروف السباق؟

هل كانت مفيدة؟

المحلول

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

افترض أن لدينا بعض الرمز إدراج صف في قاعدة البيانات:

class TestSubject

  def insert_unless_exists
    if !row_exists?
      insert_row
    end
  end

end

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

class TestSubject

  def insert_unless_exists
    if !row_exists?
      before_insert_row_hook
      insert_row
    end
  end

  def before_insert_row_hook
  end

end

عند تشغيله في البرية، لا يوجد شيء لا يوجد شيء ما عدا أكل قليلا من وقت وحدة المعالجة المركزية. ولكن عندما يتم اختبار التعليمات البرمجية لحالة السباق، فإن تصحيحات قرد الاختبار قبل_insert_row_hook:

class TestSubject
  def before_insert_row_hook
    insert_row
  end
end

ليس هذا خبيث؟ مثل الدبابير الطفيلي يرقة التي اختطفت جثة كاتربيلر مطمئن، اختطف الاختبار الرمز قيد الاختبار حتى يخلق الحالة الدقيقة التي نحتاج إلى اختبارها.

هذه الفكرة بسيطة مثل مؤشر XOR، لذلك أظن أن العديد من المبرمجين قد اخترعها بشكل مستقل. لقد وجدت أنه مفيد بشكل عام لاختبار التعليمات البرمجية مع ظروف السباق. اتمني ان يكون مفيدا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top