• 0
Khaled Alshaya

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

سؤال

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

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

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

هل من مجيب...

تحياتي,,,

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

13 إجابة على هذا السؤال .

  • 0

سلام عليكم

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

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

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

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

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

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

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

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

2

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

شكراً يا شباب على المعلومات, و لكن أريد شيء "محسوس" :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
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

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

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


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

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

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

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 هيتم فشل

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


[[email protected] 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 فى حال عدم تحقيقها هيتم ابلاغنا بفشل فى ناتج عملية معينة


[[email protected] 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

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

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

بالنسبة للأخ أحمد, فأنا أصلاً قمت بكتابة 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

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

السلام عليكم

ال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
1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

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

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

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

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

1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

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

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

لمحبي الجافا

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

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

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

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

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

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

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

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

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

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

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

Test-Driven Development By Example ل Kent Beck

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

تم تعديل بواسطه ahmed_youssef
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

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

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

 

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

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

من فضلك سجل دخول لتتمكن من التعليق

ستتمكن من اضافه تعليقات بعد التسجيل



سجل دخولك الان

  • يستعرض القسم حالياً   0 members

    لا يوجد أعضاء مسجلين يشاهدون هذه الصفحة .