سؤال

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

ربما لم أحصل على عينة جيدة من مجموعة الوظائف، ولكن ما هي خصائص الاختبار الجيدة لهذه الوظائف، على سبيل المثال؟

//transforms [1;2;3;4] into [(1,2);(3,4)]
pairs : 'a list -> ('a * 'a) list      //'

//splits list into list of lists when predicate returns 
//  true for adjacent elements
splitOn : ('a -> 'a -> bool) -> 'a list -> 'a list list

//returns true if snd is bigger
sndBigger : ('a * 'a) -> bool (requires comparison)

لا يوجد حل صحيح

نصائح أخرى

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

  1. الخصائص الاستقرائية للوظائف العودية.بالنسبة للوظائف البسيطة، قد يكون هذا بمثابة إعادة تنفيذ التكرار.ومع ذلك، اجعل الأمر بسيطًا:بينما يتطور التنفيذ الفعلي في كثير من الأحيان (على سبيل المثال.يصبح متكررًا ذيلًا، يمكنك إضافة الحفظ،...) احتفظ بالخاصية واضحة.عادةً ما يكون مُدمج الخاصية ==> مفيدًا هنا.قد تكون وظيفة الأزواج الخاصة بك مثالاً جيدًا.
  2. الخصائص التي تحتوي على عدة وظائف في الوحدة النمطية أو النوع.هذا هو الحال عادة عند التحقق من أنواع البيانات المجردة.على سبيل المثال:إضافة عنصر إلى مصفوفة يعني أن المصفوفة تحتوي على هذا العنصر.يؤدي هذا إلى التحقق من تناسق Array.add وArray.contains.
  3. رحلات ذهابا وإيابا:وهذا جيد للتحويلات (على سبيل المثال.التحليل والتسلسل) - إنشاء تمثيل عشوائي، وإجراء تسلسل له، وإلغاء تسلسله، والتحقق من أنه يساوي الأصل.قد تتمكن من القيام بذلك باستخدام SplitOn وconcat.
  4. الخصائص العامة كما الشيكات التعقل.ابحث عن الخصائص المعروفة بشكل عام والتي قد تكون صحيحة - أشياء مثل التبادلية، والترابط، والعجز (تطبيق شيء ما مرتين لا يغير النتيجة)، والانعكاسية، وما إلى ذلك.الفكرة هنا هي ممارسة الوظيفة قليلاً - لمعرفة ما إذا كانت تفعل شيئًا غريبًا حقًا.

كنصيحة عامة، حاول ألا تجعل الأمر كبيرًا جدًا.بالنسبة إلى sndBigger، ستكون الخاصية الجيدة هي:

يجب أن تعود صحيحًا إذا وفقط إذا كان SND أكبر "(A: int) (B: ​​int) = sndbigger (a ، b) = b> a

وربما هذا هو بالضبط التنفيذ.لا تقلق بشأن ذلك - في بعض الأحيان، يكون اختبار الوحدة البسيط القديم هو ما تحتاجه تمامًا.لا ذنب ضروري!:)

ربما هذا الرابط (من قبل فريق Pex) يقدم أيضًا بعض الأفكار.

سأبدأ مع sndBigger - إنها وظيفة بسيطة للغاية، ولكن يمكنك كتابة بعض الخصائص التي يجب أن تمسك بها. على سبيل المثال، ما يحدث عند عكس القيم في Tuple:

// Reversing values of the tuple negates the result
let swap (a, b) = (b, a)
let prop_sndBiggerSwap x = 
  sndBigger x = not (sndBigger (swap x))

// If two elements of the tuple are same, it should give 'false'
let prop_sndBiggerEq a = 
  sndBigger (a, a) = false

تعديل: هذه القاعدة prop_sndBiggerSwap لا يحمل دائما (انظر التعليق بقلم). ومع ذلك يجب أن يكون ما يلي صحيحا:

// Reversing values of the tuple negates the result
let prop_sndBiggerSwap a b = 
  if a <> b then 
    let x = (a, b)
    sndBigger x = not (sndBigger (swap x))

فيما يتعلق pairs وظيفة، بقلم نشر بالفعل بعض الأفكار الجيدة. بالإضافة إلى ذلك، يمكنك التحقق من أن تحول القائمة المحول مرة أخرى إلى قائمة العناصر بإرجاع القائمة الأصلية (ستحتاج إلى التعامل مع القضية عندما تكون قائمة الإدخال غريبة - اعتمادا على ما pairs يجب أن تفعل الدالة في هذه الحالة):

let prop_pairsEq (x:_ list) = 
  if (x.Length%2 = 0) then
    x |> pairs |> List.collect (fun (a, b) -> [a; b]) = x
  else true

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

let prop_splitOnEq f x = 
  x |> splitOn f |> List.concat = x

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

let prop_splitOnAdjacentTrue f x = 
  x |> splitOn f 
    |> List.forall (fun l -> 
         l |> Seq.pairwise 
           |> Seq.forall (fun (a, b) -> f a b))

ربما آخر شيء آخر يمكن أن تحقق هو ذلك f عائدات false عندما تعطيه العنصر الأخير من قائمة واحدة والعنصر الأول من القائمة التالية. ما يلي غير مكتمل تماما، لكنه يظهر الطريق للذهاب:

let prop_splitOnOtherFalse f x = 
  x |> splitOn f
    |> Seq.pairwise 
    |> Seq.forall (fun (a, b) -> lastElement a = firstElement b)

تظهر العينة الأخيرة أيضا أنه يجب عليك التحقق مما إذا كان splitOn يمكن لوظيفة إرجاع قائمة فارغة كجزء من قائمة النتائج المرتجعة (لأنه في هذه الحالة، لم تتمكن من العثور على عنصر الأول / الأخير).

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

  • pairs
    • ما هو متوقع عندما يكون الطول الأصلي غير قابل للقسمة من قبل اثنين؟ يمكنك التحقق من رمي استثناء إذا كان هذا هو السلوك الصحيح.
    • List.map fst (pairs x) = evenEntries x و List.map snd (pairs x) = oddEntries x لوظائف بسيطة evenEntries و oddEntries التي يمكنك الكتابة.
  • splitOn
    • إذا فهمت وصفك لكيفية العمل من المفترض أن تعمل الوظيفة، فيمكنك التحقق من الشروط مثل "لكل قائمة في نتيجة splitOn f l, ، لا يوجد إدختي متتالية تلبية F "و" أخذ قوائم (l1,l2) من عند splitOn f l الزوجي، f (last l1) (first l2) يحمل ". لسوء الحظ، من المحتمل أن يكون المنطق هنا مماثلة في التعقيد إلى التنفيذ نفسه.
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top