هل هو سوء الممارسة بشكل عشوائي-توليد اختبار البيانات ؟

StackOverflow https://stackoverflow.com/questions/636353

  •  10-07-2019
  •  | 
  •  

سؤال

منذ أن كنت قد بدأت باستخدام rspec لدي مشكلة مع مفهوم المباريات.بلدي الشواغل الأساسية هي هذه:

  1. يمكنني استخدام اختبار للكشف عن سلوك مفاجئ.أنا لست دائما ذكي بما فيه الكفاية تعداد كل حالة الحافة على أمثلة انا الاختبار.باستخدام ترميز-الثابت المباريات يبدو الحد لأنه فقط الاختبارات قانون بلدي مع حالات محددة جدا التي كنت قد يتصور.(باعتراف الجميع ، مخيلتي هو أيضا الحد فيما يتعلق القضايا التي الاختبار.)

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

    describe Item do
      describe '#most_expensive' do
        it 'should return the most expensive item' do
          Item.most_expensive.price.should == 100
          # OR
          #Item.most_expensive.price.should == Item.find(:expensive).price
          # OR
          #Item.most_expensive.id.should == Item.find(:expensive).id
        end
      end
    end
    

    باستخدام الأسلوب الأول يعطي القارئ لا دلالة ما أغلى هذا البند ، إلا أن سعره 100.جميع الطرق الثلاث أطلب من القارئ أن نؤمن أن اساسيا :expensive هو أغلى واحد المدرجة في fixtures/items.yml.الإهمال مبرمج يمكن كسر الاختبارات من خلال خلق Item في before(:all), أو عن طريق إدخال آخر اساسيا في fixtures/items.yml.إذا كان هذا هو ملف كبير ، فإنه يمكن أن يستغرق وقتا طويلا لمعرفة ما هي المشكلة.

شيء واحد لقد بدأت القيام به هو إضافة #generate_random طريقة جميع النماذج.هذه الطريقة متاحة فقط عندما أنا أدير عملي المواصفات.على سبيل المثال:

class Item
  def self.generate_random(params={})
    Item.create(
      :name => params[:name] || String.generate_random,
      :price => params[:price] || rand(100)
    )
  end
end

(تفاصيل محددة عن كيف أفعل هذا في الواقع قليلا أكثر نظافة.لدي الدرجة التي تتعامل مع توليد تنظيف جميع النماذج ، ولكن هذا الرمز هو واضح بما فيه الكفاية من أجل بلدي على سبيل المثال.) لذلك في المثال أعلاه, ربما الاختبار على النحو التالي.تحذير من خدعة من القلب:بلدي رمز تعتمد بشكل كبير على استخدام before(:all):

describe Item do
  describe '#most_expensive' do
    before(:all) do
      @items = []
      3.times { @items << Item.generate_random }
      @items << Item.generate_random({:price => 50})
    end

    it 'should return the most expensive item' do
      sorted = @items.sort { |a, b| b.price <=> a.price }
      expensive = Item.most_expensive
      expensive.should be(sorted[0])
      expensive.price.should >= 50      
    end
  end
end

هذه الطريقة ، اختباراتي أفضل تكشف عن سلوك مفاجئ.عندما تولد البيانات بهذه الطريقة ، أنا أحيانا تتعثر في قضية حافة أين قانون بلدي لا تتصرف كما هو متوقع ، ولكن الذي لم أكن قد اشتعلت لو كنت فقط باستخدام جدول المباريات.على سبيل المثال ، في حالة #most_expensive, إذا نسيت أن التعامل مع حالة خاصة حيث عناصر متعددة حصة أغلى الأسعار ، اختباري أحيانا تفشل في أول should.رؤية غير حتمية الفشل في AutoSpec أن تدخلني في أن شيئا ما كان خطأ.لو كنت فقط باستخدام تركيبات ، فإنه قد يستغرق وقتا أطول بكثير لاكتشاف هذا الخلل.

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

هل هذا سوء الممارسة ؟ هو خوفي من تركيبات غير عقلاني ؟ هو كتابة generate_random الطريقة لكل نموذج الكثير من العمل ؟ أو يعمل هذا ؟

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

المحلول

هذا هو الجواب على النقطة الثانية:

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

وأنا أتفق.من الناحية المثالية المواصفات الأمثلة ينبغي أن تكون مفهومة من قبل أنفسهم.استخدام تركيبات إشكالية لأنه يقسم شروط مسبقة على سبيل المثال من النتائج المتوقعة.

بسبب هذا, العديد من RSpec المستخدمين قد توقفت عن استخدام تركيبات تماما.بدلا من ذلك, بناء على حاجة الكائنات في المواصفات سبيل المثال نفسها.

describe Item, "#most_expensive" do
  it 'should return the most expensive item' do
    items = [
      Item.create!(:price => 100),
      Item.create!(:price => 50)
    ]

    Item.most_expensive.price.should == 100
  end
end

إذا كان الخاص بك في نهاية المطاف مع الكثير من رمز المتداول على وجوه الخلق ، يجب أن نلقي نظرة على بعض من العديد من كائن الاختبار مصنع المكتبات مثل factory_girl, الماكنه, أو FixtureReplacement.

نصائح أخرى

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

لقد فكرت كثيرا في الآونة الأخيرة المشروع من الألغام.في النهاية اتفقنا على نقطتين:

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

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

الكثير من المعلومات الجيدة وقد تم بالفعل نشر, ولكن انظر أيضا: زغب الاختبار.كلمة في الشارع هو أن مايكروسوفت يستخدم هذا النهج على الكثير من المشاريع.

تجربتي مع اختبار الغالب بسيطة مع البرامج المكتوبة في C/Python/Java, لذلك أنا لست متأكدا إذا كان هذا هو تنطبق تماما, ولكن كلما كان لدي برنامج التي يمكن أن تقبل أي نوع من المستخدم إدخال أنا دائما تشمل اختبار عشوائي إدخال البيانات ، أو على الأقل إدخال البيانات التي تم إنشاؤها بواسطة الكمبيوتر بطريقة لا يمكن التنبؤ بها ، لأنك لا يمكن أبدا افتراضات حول ما سيكون للمستخدمين الدخول.أو حسنا ، يمكن, ولكن إذا كنت تفعل ذلك الحين بعض القراصنة الذين لا يجعل هذا الافتراض قد تجد الخلل التي تجاهلها تماما.آلة ولدت الإدخال هو أفضل (فقط؟) طريقة أعرف من الحفاظ على التحيز الإنسان تماما من إجراءات الاختبار.بالطبع ، من أجل إعادة إنتاج فشل الاختبار عليك أن تفعل شيئا مثل إنقاذ اختبار المدخلات إلى ملف أو طباعة من ذلك (إذا كان النص) قبل تشغيل الاختبار.

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

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

ثم التحول من اختبار عشوائي إلى الاختبار الإحصائي.

if (a > 0)
    // Do Foo
else (if b < 0)
    // Do Bar
else
    // Do Foobar

إذا قمت بتحديد a و b بشكل عشوائي في int مجموعة, يمكنك ممارسة الرياضة Foo 50 ٪ من الوقت ، Bar 25% من الوقت ، Foobar 25% من الوقت.فمن المحتمل أنك سوف تجد المزيد من الخلل في Foo من Bar أو Foobar.

إذا قمت بتحديد a هذا هو سلبي 66.66% من الوقت ، Bar و Foobar الحصول على ممارسة المزيد من أول التوزيع.في الواقع ثلاثة فروع على ممارسة كل 33.33% من الوقت.

بالطبع, إذا لاحظ النتيجة مختلفة من النتيجة المتوقعة ، عليك أن سجل كل ما يمكن أن يكون من المفيد إعادة علة.

أود أن أقترح وجود نظرة على الماكنه:

http://github.com/notahat/machinist/tree/master

الماكنه توليد البيانات بالنسبة لك ولكنها غير قابلة للتكرار ، لذلك كل اختبار تشغيل نفس بيانات عشوائية.

هل يمكن أن تفعل شيئا مماثلا من قبل بذر مولد رقم عشوائي باستمرار.

مشكلة واحدة مع بشكل عشوائي حالات الاختبار هو أن التحقق من صحة الجواب يجب أن يكون محسوب من قبل رمز و لا يمكنك أن تكون متأكدا من أنه لا يكون الخلل :)

قد ترى أيضا هذا الموضوع: اختبار عشوائي المدخلات أفضل الممارسات.

فعالية هذا الاختبار يعتمد إلى حد كبير على نوعية مولد رقم عشوائي يمكنك استخدام وكيف الصحيح هو الرمز الذي يترجم RNG الإخراج إلى بيانات الاختبار.

إذا RNG لم تنتج القيم مما تسبب في التعليمات البرمجية الخاصة بك في حالة الحافة الحالة سوف لا يكون لديك هذه الحالة تغطيتها.إذا كان لديك التعليمات البرمجية التي يترجم RNG الإخراج إلى إدخال رمز اختبار هو خلل قد يحدث هذا حتى مع مولد كنت لا تزال لا تصل جميع الحالات الحافة.

كيف يمكنك اختبار ذلك ؟

المشكلة مع العشوائية في حالات الاختبار هو أن الناتج هو عشوائي.

الفكرة وراء الاختبارات (وخصوصا الانحدار الاختبارات) للتحقق من أن يتم كسر شيء.

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

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

هذا تخزين البيانات التي تم إنشاؤها يسمح لك العثور على 'الصحيح' ردا على هذه البيانات.

لذا أنصح باستخدام بيانات عشوائية لاستكشاف النظام الخاص بك, ولكن استخدام البيانات المحددة في الاختبارات الخاصة بك (والتي قد يكون في الأصل تم إنشاؤه بشكل عشوائي البيانات)

استخدام اختبار عشوائي البيانات هو ممارسة ممتازة-من الصعب ترميز بيانات الاختبار فقط اختبارات الحالات صراحة فكرت في حين أن بيانات عشوائية تمسح الخاص بك ضمني الافتراضات التي قد تكون خاطئة.

أنا أوصي باستخدام فتاة المصنع و ffaker هذا.(لا تستخدم القضبان تركيبات أي شيء تحت أي ظرف من الظروف.)

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