الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

ماهو ال Unit Testing و لماذا نحتاجه؟

بدأه Khaled.Alshaya في 16 فبراير 2010 · 13 رد · 9,418 مشاهدة · في هندسة البرمجيات
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم و رحمة الله و بركاته,

دائماً ما اسمع عن الـ Unit Testing و عن فوائده و لكن الحقيقة تقال, حتى الآن لم أفهم مالمقصود منه بالضبط, و كيف تكون اختباراتي لمكتبة أقوم بكتباتها Unit Testing.

قبل أن ندخل في استخدام أدوات الـ Unit Testing أود أن يعطينا أحد الأخوة أمثلة عن ماهية هذه الطريقة, و لماذا هي منتشرة بين مبرمجي العالم؟

هل من مجيب...

تحياتي,,,

#2

سلام عليكم

الهدف من ال unit testing انك تختبر ابسط جزئية فى كودك قابلة للإختبار بمفردها--من هنا جت كلمة unit -والإختبار مرتبط هل بيأدى الوظيفة بصورة سليمة او لأ

فلما تكون كل الأجزاء فى برنامجك بتأدى وظيفتها بصورة سليمة يبقة برنامجك كله هيكون بيأدى وظيفته بصورة سليمة

(map share people)

فضلا لاتقم بمراسلتي من أجل أسئلة لها أقسامها في المنتدى حتى تعم الفائدة على الجميع وللحصول على إجابات أفضل من أعضاء أكثر خبرة.
Weblog
@bitbucket
@xmonader

#3

الUnit Test هي وسيلة مفيدة جداً لعمل الاتي

1- التأكد من أن الكود الذي تكتبه ينفذ ما تريد خاصة اذا كان الكود في مكتبة ليس لها واجهة رسومية للتجربة

2- بمجرد التأكد من وجود اختبارات كافية يمكنك التحرك و التغيير بحرية داخل الكود دون خوف من أن تتسبب تغييراتك في تغيير شئ لايجب أن يتغير, لأن كل ما عليك هو أن تعيد تشغيل الUnit Tests بعد أن تقوم بتغييراتك و ان سارت على مايرام هذا يعطيك ثقة أن تغييراتك لم تعطب البرنامج الأصلي

3- مجرد التفكير في قابلية الكود لعمل Unit Testing عليه تجعلك تكتب كود أفضل و تحاول أن تحدد الDependencies التي تعتمد عليها و وضع Interfaces حولها حتى يتسنى لك عمل Mocking لها أثناء عملية الTesting فمثلاً لو كان برنامجك يعتمد على بيانات يتم قراءتها من قاعدة بيانات فأثناء عملية الUnit testing من المفضل أن لا تقرأ بياناتك من قاعدة البيانات التي تعتبر External Dependency و التي يجب أن تقوم بعزلها عن طريق وضع Interface بين المناطق التي تحتاج البيانات و مصدر البيانات و هنا يمكنك تجربة الكود عن طريق تحميلها من ملفات على الجهاز مثلاً و التأكد من أن كودك يعمل دون مشاكل دون الحاجة الى وجود قاعدة بيانات كبيرة و التي لا تحتمل أن يتم تحميلها كل مرة تقوم بتمرير الUnit tests

هناك الكثير من الفوائد و لكن ماذكرته هو ما أجد أنه مهم بالنسبة لي

3 −1

مصري في بلاد الفرنجة.

قريباً اقرأ مقالاتي على It-scoop

#4

شكراً يا شباب على المعلومات, و لكن أريد شيء "محسوس" :P

أعتقد أن مثالاً لن يضر الموضوع,

تصور أننا نقوم بكتابة دالة تقوم بإرجاع عدد العناصر في range معين, مع فرق يحدده مستخدم الدالة بين كل عنصرين متتاليين, كيف ستقوم باختبار هذه الدالة؟

int count_range_elements(first_element, last_element, step);

حبث العنصر الأول inclusive و العنصر الأخير exclusive. فمثلاً,

 int count_range_elements(first_element, last_element, step);

int count = count_range_elements(0, 10, 1); // count == 10 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
int count = count_range_elements(0, 10, 3); // count == 4  [0, 3, 6, 9]

هل ستضع بعض الأمثلة التي قمت بحلها يدوياً, و تتأكد من أن الدالة تنتج الإجابة الصحيحة لتلك الأمثلة, أم أنك تقوم بوضع الاختبارات بطريقة أخرى؟

هذا هو سؤالي, بالنسبة للـ integration tests فهذا موضوع سابق لأوانه :)

تحياتي,

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 16 فبراير 2010 في 09:02

#5
Khaled.Alshaya كتب:

هل ستضع بعض الأمثلة التي قمت بحلها يدوياً, و تتأكد من أن الدالة تنتج الإجابة الصحيحة لتلك الأمثلة, أم أنك تقوم بوضع الاختبارات بطريقة أخرى؟

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

int count = count_range_elements(0, 10, 1);
int count = count_range_elements(0, 9, 2);
int count = count_range_elements(10, 0, 1);
int count = count_range_elements(0, 10, -1);
int count = count_range_elements(10, 0, -1);
int count = count_range_elements(9, 0, -2);
1
#6

طب انا هتكلم عن مثالين

1- ب doctest

def count_els_in_range(start, end, step):
    """
    >>> count_els_in_range(0, 10, 1) # count == 10 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    10
    >>> count_els_in_range(0, 10, 3) # count == 4  [0, 3, 6, 9]
    4
    >>> count_els_in_range(0, 10, 4) # count == 5  
    5

    """
    return len(range(start, end, step))

هنا انا دمجت ال test فى توثيق الدالة -docstrings-

وكل اللى عملته انى افترضت انى فى جلسة تفاعلية مع بايثون

وكل سطر مسبوق ب <<< بيتم ادخاله لبايثون واختباره مع الناتج فى السطر الى بدون الأسهم على سبيل المثال

>>> count_els_in_range(0, 10, 1) # count == 10 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

السطر دا هيتم تنفيذه ومقارنته مع 4

طيب فى الجزئية دى

    >>> count_els_in_range(0, 10, 4) # count == 5  
    5

بالتأكيد الناتج مش هيكون 5 بأى حال من الأحوال فلما يتم مقارنته المدخل مع 5 هيتم فشل

دى شكل الإختبار

[xmonader@localhost testtut]$ python -m doctest -v test1.py 
Trying:
    count_els_in_range(0, 10, 1) # count == 10 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Expecting:
    10
ok
Trying:
    count_els_in_range(0, 10, 3) # count == 4  [0, 3, 6, 9]
Expecting:
    4
ok
Trying:
    count_els_in_range(0, 10, 4) # count == 5  
Expecting:
    5
**********************************************************************
File "test1.py", line 9, in test1.count_els_in_range
Failed example:
    count_els_in_range(0, 10, 4) # count == 5  
Expected:
    5
Got:
    3
1 items had no tests:
    test1
**********************************************************************
1 items had failures:
   1 of   3 in test1.count_els_in_range
3 tests in 2 items.
2 passed and 1 failed.
***Test Failed*** 1 failures.

الشائع هو استخدام مكتبة مشابة ل JUnit

import unittest

def count_els_in_range(start, end, step):

    return len(range(start, end, step))



class TestCase(unittest.TestCase):

    def test_count_els_in_range(self):
        self.assertEqual(4, count_els_in_range(0, 10, 3) )
        self.assertEqual(10, count_els_in_range(0, 10, 1) )

    def test_range(self):
        self.failIf(10 in range(0, 10, 1) )
        self.assert_(0 in range(0, 10, 1) )

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

[xmonader@localhost testtut]$ python test2.py -v
test_count_els_in_range (__main__.TestCase) ... ok
test_range (__main__.TestCase) ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

بخصوص ال integration testing فاللى اعرفه هو خطوة تالية بعد ضمان نجاح اختبار ال units ونقلها لإختبار الوحدات بصورة جماعية مع بعضها

1
(map share people)

فضلا لاتقم بمراسلتي من أجل أسئلة لها أقسامها في المنتدى حتى تعم الفائدة على الجميع وللحصول على إجابات أفضل من أعضاء أكثر خبرة.
Weblog
@bitbucket
@xmonader

#7

شكراً جزيلاً للجميع, أعتقد أن الفكرة شبه واضحة الآن :)

بالنسبة للأخ أحمد, فأنا أصلاً قمت بكتابة Python like range و لكن بـ ++C :P

و لهذا أسأل كيف يمكن أن يتم اختباره, هذه عينة من الاختبارات (كتبت كام macro للـ testing) و التي كنت أريد أن أتاكد أنها بالطريقة "الصحيحة" على الأقل:

void range_test_constructors()
{
	std::clog
		<< std::string(80, '=')
		<< "range_test_constructors();" << std::endl
		<< std::string(80, '=');

	range r1(0, 10, 1), r2(r1);

	must_not_throw_exception( range() );
	must_throw_exception( range(1, 10, 0) );
	must_throw_exception( range(0, 10, -1) );
	must_throw_exception( range(10, 0, 1) );
	must_not_throw_exception( range(10, 10, 1) );
	must_not_throw_exception( range(10, 10, -1) );
	should_succeed( r1 == r2 );
}

void range_test_operators()
{
	std::clog
		<< std::string(80, '=')
		<< "range_test_operators();" << std::endl
		<< std::string(80, '=');

	range r1(0, 10, 1), r2;

	r2 = r1;
	should_succeed( r1 == r2 );
	r1 = range(1, 10, 1);
	should_succeed( r1 != r2 );
	should_succeed( range(0, 10, 1) < range(1, 11, 1) );
	should_succeed( range(0, 10, 1) <= range(0, 10, 1) );
	should_succeed( range(1, 11, 1) > range(0, 10, 1) );
	should_succeed( range(0, 10, 1) >= range(0, 10, 1) );
}

void range_test_query_members()
{
	std::clog
		<< std::string(80, '=')
		<< "range_test_query_members();" << std::endl
		<< std::string(80, '=');

	range r(0, 10, 2);
	should_succeed( r.size() == r.max_size() && r.size() == 5 );
	r = range();
	should_succeed( r.empty() );
}

هل يسمى هذا unit testing :P

#8

السلام عليكم

الunit testing فائدة مثلما قال الاخ احمد ولكن تختلف اساليبة واشكالة

اول اسلوب وهو طريقة سطحية هى طريقة اختبار مميزات البرنامج وخصائصة وتفعل عندما لا نريد احتكاك المختبر بالكود او ان الكود يجب ان يكون مغلقا بالنسبة لة وتسمى تلك الطريقة end to end testing

endtoend.png

وهناك طريقة اخرى واعمق وهى على مستوى الكود بحيث تعزل كل وحدة (module or component) من الكود وتقوم باختبار بعدة اساليب

unit.png

وتلك الطريقة تقوم بالتحقق من ان كل الinput الممكنة تقوم باخراج الoutput المتوقع .

فى تلك الطريقة هناك عدة طرق

-يمكنك كتابة بعض دوال الاختبار يدويا لتقوم بتجربتها بشكل متكرر فى الكود بحيث تختبر الدالة

مثلا

void test1( void )
{
  stack_t myStack;
  int ret;
  failed = 0;
  ret = stackCreate( 0, 0 );
  checkResult( 0, (ret == -1) );
  ret = stackCreate( &myStack, 0 );
  checkResult( 1, (ret == -1) );
  ret = stackCreate( &myStack, 65536 );
  checkResult( 2, (ret == -1) );
  ret = stackCreate( &myStack, 1024 );
  checkResult( 3, (ret == 0) );
  checkResult( 4, (myStack.state == STACK_CREATED) );
  checkResult( 5, (myStack.index == 0) );
  checkResult( 6, (myStack.max == 1024) );
  checkResult( 7, (myStack.storage != (int *)0) );
  ret = stackDestroy( 0 );
  checkResult( 8, (ret == -1) );
  ret = stackDestroy( &myStack );
  checkResult( 9, (ret == 0) );
  checkResult( 10, (myStack.state != STACK_CREATED) );

  if (failed == 0) printf( "test1 passed.\n");
   else printf("test1 failed\n");
 }

وتكون دالة الاختبار

int failed;
void checkResult( int testnum, int result )
{
  if (result == 0) {
    printf( "*** Failed test number %d\n", testnum );
    failed++;
  } else {
    printf( "Test number %2d passed.\n", testnum );
  }
}

طبعا البرنامج يرسل لبرنامج الاختبار الinput و bool المتوقع من تحقق الoutput

وسيكون الخارج على هذا الشكل

Test number 0  passed.
Test number 1  passed.
Test number 2  passed.
Test number 3  passed.
Test number 4  passed.
Test number 5  passed.
Test number 6  passed.
Test number 7  passed.
Test number 8  passed.
Test number 9  passed.
Test number 10 passed.
test1 passed.

ويمكنك عزل كود الاختبار فى ملف منعزل وليكن test.c .

#include <stdio.h>
#include "stack.h"
#include "cut.h"
stack_t myStack_1;
stack_t myStack_2;
void __CUT_BRINGUP__Explode( void )
{
  int ret;
  printf("Stack test bringup called\n");
  ret = stackCreate( &myStack_1, 5 );
  ASSERT( (ret == 0), "Stack 1 Creation." );
  ret = stackCreate( &myStack_2, 5 );
  ASSERT( (ret == 0), "Stack 2 Creation." );
}
void __CUT__PushConsumptionTest( void )
{
  int ret;
  /* Exhaust the stack */
  ret = stackPush( &myStack_1, 1 );
  ASSERT( (ret == 0), "Stack Push 1 failed." );
  ret = stackPush( &myStack_1, 2 );
  ASSERT( (ret == 0), "Stack Push 2 failed." );
  ret = stackPush( &myStack_1, 3 );
  ASSERT( (ret == 0), "Stack Push 3 failed." );
  ret = stackPush( &myStack_1, 4 );
  ASSERT( (ret == 0), "Stack Push 4 failed." );
  ret = stackPush( &myStack_1, 5 );
  ASSERT( (ret == 0), "Stack Push 5 failed." );
  ret = stackPush( &myStack_1, 6 );
  ASSERT( (ret == -1), "Stack exhaustion failed." );
}
void __CUT__PushPopTest( void )
{
  int ret;
  int value;
  /* Test two pushes and then two pops */
  ret = stackPush( &myStack_2, 55 );
  ASSERT( (ret == 0), "Stack Push of 55 failed." );
  ret = stackPush( &myStack_2, 101 );
  ASSERT( (ret == 0), "Stack Push of 101 failed." );
  ret = stackPop( &myStack_2, &value );
  ASSERT( (ret == 0), "Stack Pop failed." );
  ASSERT( (value == 101), "Stack Popped Wrong Value.");
  ret = stackPop( &myStack_2, &value );
  ASSERT( (ret == 0), "Stack Pop failed." );
  ASSERT( (value == 55), "Stack Popped Wrong Value." );
}
void __CUT_TAKEDOWN__Explode( void )
{
  int ret;
  ret = stackDestroy( &myStack_1 );
  ASSERT( (ret == 0), "Stack 1 Destruction." );
  ret = stackDestroy( &myStack_2 );
  ASSERT( (ret == 0), "Stack 2 Destruction." );
  printf( "\n\nTest Complete\n");
}

وعمل main للاختبار او استعمال احد اسكربتات التى تقوم باخراج main لاختبار الملف مثلC Unit Tester (ستجد الملف cut.h المستعمل فى هذا المشروع فى ملفات الاسكربت)

وتقوم بالعمل بتلك الطريقة

cutgen.py test_1.c > cutcheck.c

حيث سيخرج لك كود الاختبار على هذا الشاكلة cutcheck.c

#include "libcut.inc"
extern void __CUT_BRINGUP__Explode( void );
extern void __CUT__PushConsumptionTest( void );
extern void __CUT__PushPopTest( void );
extern void __CUT_TAKEDOWN__Explode( void );
int main( int argc, char *argv[] )
{
  cut_init( -1 );
  cut_start( "Explode", __CUT_TAKEDOWN__Explode );
  __CUT_BRINGUP__Explode();
  __CUT__PushConsumptionTest();
  __CUT__PushPopTest();
  cut_end( "Explode" );
  __CUT_TAKEDOWN__Explode();
  cut_break_formatting();
  return 0;
}

وتقوم بترجمة الكود

gcc -o cutcheck stack.c test_1.c cutcheck.c

وتقوم بتشغيل الملف وتشاهد output الملف التنفيذى .

وهناك ادوات واسكربتات اخرى للمساعدة فى ذلك مثل expect utility وهناك ادوات اذا كنت لا تعتمد على المكتبة القياسية مثلما يفعل البعض على الانظمة المضمنة مثل Embunit

تم تعديل هذه المشاركة بواسطة apex في 16 فبراير 2010 في 17:27

1

name : mohamedyosry

#9

شكرا للجميع .. نقاش جميل ومفيد ..

يعني بالاخير .. ممكن ان نقول بانها Trace (( تتبع للكود )) لكن بشكل متطور .؟؟؟

كما هو الحال في برولوك .. نقوم بعمل التتبع للكود فيقوم بتنفيذه جزء جزء ..

صحيح .. استفسار .. الUnit Testing موجودة في الدوت نت .. طيب هل هي مرفقة مع الدوت نت فقط ام هي عامة ؟؟

1

يَارَبُ إِن ضَاقَت قُلُوُب الْنَّاسٍ عَنْ مّافِي .. مِنْ خَيْرٍٍ فَعَفْوكَ لَا يَضِيْقْ ..

#10

بإمكانك استخدام NUnit مع الدوت نيت كإطار عمل لاختبار الكود

Technical Lead Developer

My LinkedIn Profile

اللهم قنى شر الجهل و الجهلاء

( اقْتَرَبَ لِلنَّاسِ حِسَابُهُمْ وَهُمْ فِي غَفْلَةٍ مَّعْرِضُونَ ) {الأنبياء:1}

#11

لمحبي الجافا

مجموعة دروس للمبتدئين في الجافا يتم استعمال الـ Junit لدى كتابة كل Class

http://eclipsetutorial.sourceforge.net/totalbeginner.html

صراحة أرى أن الأمر مبالغ فيه في بعض الأحيان :D

#12

شكراً للجميع,

أخ أحمد يوسف, المثال رائع, أنا أحاول حالياً أن أفهم الفكرة نفسها, و بعدها أريد تعلم Boost.Test

هل تعرف عن مقالات هو هذا الأمر, أي مصادر...؟

شكراً من جديد أخ أحمد,

#13

بلاش تفكر فى الموضوع اكتر مما يستحق لأنك محتاج مجرد معرفة بالأساسيات وازاى تعمل ال tests مش اكتر .. --اكيد هتوافينا بمعلومات عن Boost Test فى اقرب وقت :)

-- اذا عايز كتاب عن ال TDD

Test-Driven Development By Example ل Kent Beck

وتحت امرك ياباشا

تم تعديل هذه المشاركة بواسطة ahmed_youssef في 19 فبراير 2010 في 09:46

(map share people)

فضلا لاتقم بمراسلتي من أجل أسئلة لها أقسامها في المنتدى حتى تعم الفائدة على الجميع وللحصول على إجابات أفضل من أعضاء أكثر خبرة.
Weblog
@bitbucket
@xmonader

#14

الحقيقه جميع الاجوبه صحيحه ، وانا سوف ازودك بمثال حقيقي عن ال unit testing باستخدام الjava 

الرجاء مشاهده الفيديدو التالي : 

 

التعليق بل الغه الانكيليزيه لو اردت بعيد الشرح بل العربي 

مواضيع مشابهة