هذه المقالة موجهة لكل مبرمج و مطور اختبارات وكل شخص يعمل ضمن فريق عمل لتطوير مشروع برنامج ما. وآمل أن تكون مصدر مساعد لاختبار برنامج الأمين9.0 في المستقبل.

في هذه المقالة سنرى كيفية عمل (TDD – Test Driven Development) بمثال عملي. سنبدأ بمقدمة عن ضمان الجودة والاختبار في تطوير البرمجيات. بعد ذلك سنصمم نظام حل متكامل من بدايته لنهايته باستخدام فلسفة (TDD). سنقوم باستخدام (unit tests) وأداة (WatiN) لتطوير اختبار يختبر واجهة تطبيق ويب.

المثال يستخدم :

  • Microsoft Visual Studio 2008
  • C# 3.0
  • Linq-to-SQL
  • Linq-to-Objects
  • Visual Studio Unit Tests
  • ASP.NET
  • WatiN

مقدمة

كثيراً ما نسمع عن الاختبار الآلي والتطوير المقاد بالاختبار (Test Driven Development) سنتكلم في هذه المقالة عن الطريقة التي سيتم إتباعها لاختبار تطبيق ويب مع مثال عملي كامل مترافق بالصور.

تابع الحوار التالي:

مدير المشروع: أيها المسؤول ألن ننهي هذا المشروع الضخم والهام في عدة أسابيع؟ زبائننا أخبرونا أكثر من مرة أنهم بحاجة لمنتج عالي الجودة. ماذا علينا أن نفعل ؟

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

مدير المشروع: جميعنا سيفعل ما بوسعه لكني قرأت في كثير من الكتب أن المبرمجين ليسوا ملزمين باختبار نصهم البرمجي الخاص بهم.

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

ما الخطأ هنا؟

مدير المشروع والمسؤول عنه يريدان مستوىً عالياً من الجودة (بغض النظر عن معنى كلمة “عالياً” هنا) في هذا المشروع الهام.

على كل حال هم يتكلمون فقط عن الاختبار! بحسب خبرتي -الكاتب قائلاً- الكثير من الناس في عالم البرمجيات يستخدمون كلمة “اختبار” ككلمة مجردة لـ “ضمان الجودة”. وفي الحقيقة الاختبار هو جزء من جميع المعايير التي يمكن استخدامها للتأكد من الجودة المطلوب في حالة محددة.

إذا كنت فعلاً تريد أن تضمن تحقيق مستوى معين من الجودة عليك أن تعيد النظر بتسلسل عملية التطوير كاملةً :

كيف تكتب التصريحات؟

أنا أفضل اتباع طريقة  التوسع في التفكير. وضّح وحدد النظام الذي تريد بناؤه كاملاً على مستوى عالٍ من التجريد. بعد ذلك قم بتشكيل فرق مخصصة من المحللين و المطورين والمختبرين وقم بتوثيق كل خطوة بعد الأخرى.

  •  احذر من الإفراط في الهندسة

    لماذا لا نقوم بتحسين خوارزمية العمل المنطقي يوماً ما قبل التسويق؟ لا يوجد مشاكل بالأداء في هذا القسم – “النظام ليس أنيقاً بشكلٍ كافٍ” هذا التفكير يجب أن لا يحدث أبداً.

  • كن متأكداً من أن الجميع يعلم مدى أهمية ضمان الجودة

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

  • كن متأكداً من أن القواعد التي يجب أن يلتزم بها المطورون و المختبرون قد تم تحديدها بوضوح

    على المطورين أن يكافحوا للوصول إلى نص برمجي رائع و خالٍ من الأخطاء حتى لو أن المختبرين ألقوا نظرة على العمل الذي أنجزوه قبل أن يصل إلى الزبون.

  • اجمع البيانات واستخدمها

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

الاختبار

كلما تكلمنا عن الاختبار في عالم صناعة البرمجيات نعني عادة طريقة تشغيل البرنامج بهدف اكتشاف الأخطاء. يمكنك تصنيف عدة أنواع للأخطاء (أنا متأكد من أن لديك تصنيفات أخرى أو طرق أخرى لتصنيف الأخطاء) .

  • الأخطاء اللغوية

    يعالجها المترجم.

  • أخطاء زمن التشغيل

    يمكن للبرنامج أن يظهر نوعاً من التصرفات غير المرغوبة (مثل: انهيار النظام – ضياع في الذاكرة …. ).

  • الأخطاء المنطقية

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

  • الأخطاء التصميمية

    وهي أخطاء في التصميم العام للنظام والتي تعتبر المبدأ الأساسي لعمل النظام.

  • مشاكل الاستخدام

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

  • الأخطاء التراجعية

    وهي الأخطاء التي لم تكن موجودة بالنسخ السابقة أو لم يتم إصلاحها سابقاً.

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

إذا كان منتجك البرمجي ناجحاً سوف تجد الكثير من الزبائن. وكنتيجة طبيعية الاختبار سيكون أكثر أهمية لأن كل خطأ في نسخة من نسخ البرنامج يظهر عند الزبائن مكلف أكثر من تصحيح خطأ يظهر في مرحلة الاختبار أثناء التطوير.

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

Typical development of failure rate (y-coordinate) of systems over time (x-coordinate).

إنك ضمن حلقة مفرغة ! إن إنتاج برنامج على مستوى عالٍ من الجودة يجعلك ناجحاً. والنجاح الأكبر والأصعب هو أن تحافظ على هذا المستوى العالي للجودة. إذا كنت تريد أن تكون ناجحاً في مجال البرمجيات لفترة طويلة يجب أن يكون لديك إستراتيجية للتعامل مع هذا الوضع.

الاختبار الآلي و التطوير المقاد بالاختبار

الفكرة بسيطة: الاختبار لن يكون يدوياً من قبل البشر بل آلياً بواسطة برنامج اختبار آلي. إن الجزء الأصعب هو تصميم الاختبارات الآلية. مبدئياً الطريقة الأسهل لتصميم اختبار آلي هي التقاط جميع الخطوات التي يقوم بها المختبر البشري وأضف بعض الشيفرات التصحيحية وأعد الاختبار عدة مرات.هذه طريقة ممكنة لأتمتة الاختبار وهناك الكثير من البرامج في الأسواق تدعم سيناريو الإعادة والالتقاط هذا.
إذا كنت تبني تطبيقات انترنت تعتمد على تقنية Microsoft’s ASP.NET فعليك استخدام Visual Studio Web Tests.
Microsoft Visual Studio يحتوي على مسجل يستطيع التقاط جميع طلبات بروتوكول HTTP والتي ترسل إلى مخدم الانترنت لتشغيل برنامجك. بعد أن تسجل حركات برنامجك يمكنك أن تعزز خوارزمية الاختبار ببعض المنطقية (مثل: التأكد من الصحة، استخلاص قيم المتحولات، ….. ) وبإمكانك جعل اختبارك أكثر شمولية (مثل: قراءة بيانات الاختبار من قاعدة بيانات، واستخدامها كبيانات دخل لحالات الاختبار). في الحقيقة هذه الطريقة تعمل بشكل جيد. على كل حال هنالك بعض المعوقات إذا قررت أن تعمل بـ Visual Studio Web Tests :

  1.  أنت بحاجة لـ Visual Studio Team System 2008 Test Edition أما إصدار Professional Edition فهو غير كافٍ.
  2. برنامجك يجب أنا يكون موجوداً قبل عملية التقاط وتسجيل الاختبار (نظرياً بالإمكان بناء اختبارات web يدوياً ولكن باعتقادي ذلك غير ملائم).
  3. كلما قمت بتغيير ضمن برنامجك عليك إعادة تسجل اختباراتك.
  4. إن Visual Studio تحاكي وجود المستعرض فقط بإرسال طلب HTTP إلى مخدم الـ web. نصوص جافا والمحتويات الفعالة التي عادة يتم تنفيذها بشكل طبيعي من قبل المستعرض يتم تجاهلها بشكل كامل. ولذلك لا يمكن استخدام Visual Studio Web Tests للاختبار الآلي لـ JavaScript code.
  5. لبعض الأسباب مسجل اختبارات الـ web لا يعمل على بعض محطات التطوير. ولا نعلم ما هي الأسباب.

إذا كنت حقاً تريد أن تقوم باختبار آلي لتسلسل تطوير برنامجك فإن العائق الأكبر للاختبار بواسطة Visual Studio Web Tests هو الثاني : عليك أن تقوم بتنفيذ برنامجك قبل أن تبدأ بإنشاء الاختبارات.
إن فكرة تسلسل هذه العملية لتطوير الاختبارات الآلية (وكذلك تدعى ” unit tests”) تدعى “Test Driven Development” واختصارها (TDD): 

هذا النوع من عملية التطوير يقدم لك الكثير من المزايا بدلاً من طريقة الاختبار التقليدية الأولية. وهذه بعضاً منها:

  • إذا كان أحد المطورين يعمل ضمن العملية بأسرها فعليه أن يقوم بالتصريح (تصميم الميزة ضمن المشروع أو البرنامج) قبل كتابة الاختبار المتعلق بها. هناك احتمال وارد جدا أن تواجه تضارب بين التصريحات وكتابة الاختبارات.
  • إذا كان أكثر من مطور يتعاملون مع ميزة واحدة (وظيفة يقوم بها البرنامج) فباستطاعتهم أن يفصلوا تطوير الاختبار عن شيفرة التطبيق بشكلٍ متوازٍ. وبهذه الحالة كل منهم يترجم هذه الميزة باستقلالية عن الآخر.

لا تستخف بالنجاح الذي حققته عند رؤية اجتياز الاختبار بنجاح بعد كتابة شيفرات التطبيق. أنا أضمن لك أن تشاهد وجوه مبتسمة في مكتبك فقط لأن الـ Visual Studio أظهر علامة التصحيح الخضراء في اختباراتهم. عادةً من الممكن لمعظم الناس اعتبار الـ (TDD) تعمل بطريقة الوصول للبيانات والتعامل مع طبقة العناصر المنطقية. على كل حال فإن استخدام أدوات الـ (TDD) الصحيحة يمكن أن يكون طريقة قوية جداً في تطوير بنية واجهات المستخدم أيضاً. فيما تبقى من هذه المقالة أريد أن أريك كيفية عمل الـ (TDD) من خلال نظام متكامل من البداية للنهاية يتضمن واجهة مستخدم تعتمد على الـ web بلغة (ASP.NET). المثال يستخدم الأدوات والتقنيات التالية:

الأدوات:

  • Visual Studio 2008 Professional Edition.
  • CaseComplete by Serlio Software.
  • WatiN for UI test automation.

التقنيات:

  • C# 3.0 as the programming language
  • SQL Server for data storage
  • Linq-to-SQL for data access
  • Linq-to-objects in test implementation
  • ASP.NET for building the web UI

المثال القسم الأول – مرحلة التحضير

سنبدأ مثالنا للتطوير المقاد بالاختبار (Test Driven Development) بالتصريحات.
سنفترض بأننا نتلقى التصريحات من محللي النظام.  تقول هذه الميزة (وظيفة يقوم بها البرنامج) بأننا بحاجة لبناء تطبيق web من أجل تتبع الوقت. ولحسن الحظ المحللين قد عرّفوا بنية بيانات البرنامج بشكل مفصل جداً. ليس لدينا مشاكل من ترجمة (تحويل) بنية بيانات البرنامج الى بعض جداول SQL Server.

Copy

1

USE [TddSample_TimeTracking]

2

GO

3

4

CREATE TABLE [dbo].[Project]

5

(

6

[ProjectId] [int] IDENTITY(1,1) NOT NULL,

7

[ProjectCode] [varchar](20) NOT NULL,

8

[ProjectDescription] [nvarchar](max) NULL,

9

CONSTRAINT [PK_Project] PRIMARY KEY CLUSTERED

10

( [ProjectId] ASC )

11

)

12

13

CREATE UNIQUE NONCLUSTERED INDEX [IX_Project_ProjectCode] ON [dbo].[Project]

14

( [ProjectCode] ASC )

15

16

CREATE TABLE [dbo].[TaskType]

17

(

18

[TaskTypeId] [int] IDENTITY(1,1) NOT NULL,

19

[TaskTypeCode] [varchar](20) NOT NULL,

20

[TaskTypeDescription] [nvarchar](max) NULL,

21

CONSTRAINT [PK_TaskType] PRIMARY KEY CLUSTERED

22

( [TaskTypeId] ASC )

23

)

24

25

CREATE UNIQUE NONCLUSTERED INDEX [IX_TaskType_TaskTypeCode] ON [dbo].[TaskType]

26

( [TaskTypeCode] ASC )

27

28

CREATE TABLE [dbo].[User]

29

(

30

[UserId] [int] IDENTITY(1,1) NOT NULL,

31

[WindowsLogin] [varchar](128) NOT NULL,

32

[CurrentStartTime] [datetime] NULL,

33

CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED

34

( [UserId] ASC )

35

)

36

37

CREATE UNIQUE NONCLUSTERED INDEX [IX_User_WindowsLogin] ON [dbo].[User]

38

( [WindowsLogin] ASC )

39

40

CREATE TABLE [dbo].[TimeTracking]

41

(

42

[TrackingId] [int] IDENTITY(1,1) NOT NULL,

43

[ProjectId] [int] NOT NULL,

44

[TaskTypeId] [int] NOT NULL,

45

[UserId] [int] NOT NULL,

46

[StartTime] [datetime] NOT NULL,

47

[EndTime] [datetime] NOT NULL,

48

[Comment] [nvarchar](max) NULL,

49

CONSTRAINT [PK_TimeTracking] PRIMARY KEY CLUSTERED

50

( [TrackingId] ASC )

51

)

52

53

ALTER TABLE [dbo].[TimeTracking] WITH CHECK ADD CONSTRAINT [FK_TimeTracking_Project] FOREIGN KEY([ProjectId]) REFERENCES [dbo].[Project] ([ProjectId])

54

55

ALTER TABLE [dbo].[TimeTracking] CHECK CONSTRAINT [FK_TimeTracking_Project]

56

57

ALTER TABLE [dbo].[TimeTracking] WITH CHECK ADD CONSTRAINT [FK_TimeTracking_TaskType] FOREIGN KEY([TaskTypeId])

58

REFERENCES [dbo].[TaskType] ([TaskTypeId])

59

60

ALTER TABLE [dbo].[TimeTracking] CHECK CONSTRAINT [FK_TimeTracking_TaskType]

61

62

ALTER TABLE [dbo].[TimeTracking] WITH CHECK ADD CONSTRAINT [FK_TimeTracking_User] FOREIGN KEY([UserId]) REFERENCES [dbo].[User] ([UserId])

63

64

ALTER TABLE [dbo].[TimeTracking] CHECK CONSTRAINT [FK_TimeTracking_User]

Business data model in the specification

 Detailed specification of entities

بالإضافة لطبقة البيانات فإن الميزات تتطلب طبقة وصول للبيانات من أجل الاستعلام عن جميع عمليات الإقحام والتعديل والحذف ضمن كائن التتبع الزمني لهذه العمليات. يبدو أن المحللين الذين كتبوا هذه الميزة (وظيفة يقوم بها البرنامج) كان لديهم معرفة بسيطة بالـ IT أليس كذلك؟ نحن قررنا استخدام Linq-to-SQL لبناء (ترجمة) طبقة الوصول للبيانات. بشكل عام ضمن TDD علينا أن نكتب الاختبارات للميزات التي نركز عليها حالياً. علينا كتابة النص البرمجي للاختبار الذي لن نقوم بعملية ترجمته لأنه حتى الآن ليس لدينا الأصناف التي تمثل هذه الكينونات. ولأكون صادقاً في مثل هذه الحالات أنا أتبع طريقة أقرب للواقع وأستخدم مولد البيانات (The Code Generator of Linq-to-SQL) قبل كتابة النص البرمجي للاختبار. إن إتباع هذه الطريقة يؤمن أشياء مريحة كالمدقق الذكي (IntelliSense). في حالتنا:

  • نبدأ بإنشاء النظام بشكل فارغ.
  • نضيف إليه مكتبة الأصناف (هذه المكتبة ستحتوي على النص البرمجي لطبقة الوصول للبيانات).
  • توليد طبقة الربط مع البيانات (Linq-to-SQL) من أجل الوصول لجداول قاعدة البيانات.

Data access layer requirements

 Linq-to-SQL data

لاحظ أني لم أكتب سطراً واحداً بنفسي . كل ذلك كان بواسطة مولد النص البرمجي لـ Linq-to-SQL. وفقاً لـ TDD أنا الآن أبدأ بكتابة أول وحدة اختبار (Unit Test) والتي ستتفحص أي عملية إضافة أو تعديل أو حذف من أجل كينون التتبع الزمني لهذه التعليمات. أنا لن أكتب النص البرمجي الذي يختبر صحة عملية قراءة محتويات هذه الكينونات (الجداول هنا) لأنني لست ملزماً بكتابة النص البرمجي لهذه الوظيفة. إنها مؤمنة بواسطة Linq-to-SQL وأنا لا أريد اختبار تطبيق Linq-to-SQL لشركة Microsoft. على كل حال عليك أن تقرر مدى التوسع بحالات اختبارك بالاعتماد على مستوى الجودة الذي تريده في مشروعك.

في حالتنا:

  • نحن أضفنا اختبار وحدة للمشروع.
  • بعض التنظيف (التخلص من الملفات النصية)
  • إعادة تسمية الوحدة UnitTest1.cs إلى DataAccessLayerTests.cs.
  • إضافة بعض الارتباطات اللازمة لبناء الاختبارات:
  • Linq-to-SQL classes ارتباط بمكتبة الأصناف بواسطة أصناف 
  • System.Data. Linq
  • System.Configuration (needed to read App.config file)
  • System.Data

المثال القسم الثاني – وحدة الاختبار الرئيسية لطبقة الوصول للبيانات

في الوقت الحالي لكتابة اختبارنا الأول. أريد لفت الانتباه إلى أن كل اختبار له استقلالية عن الآخر ويجب أن يكون بعيد عن الشروط المسبقة قدر الإمكان. ولذلك أنا استخدمت الـوظائف والتوابع التمهيدية (initialization functions) لـِ Net. لكي أجهز جداول قاعدة البيانات عن طريق تعريف مجموعة من اختبارات البيانات قبل تشغيل أي اختبار. إن الـ initialization functions موجودة ضمن صنف في مستوى – طبقة – الاختبار. ولذلك فإن في مستوى – طبقة – الأصناف يجب أن تزود وتنمق بـ ClassInitialize/ClassCleanup/TestInitialize/TestCleanup .

الإجراءات التمهيدية وإجراءات التحرير لضمان شروط مسبقة واضحة المعالم قبل الشروع بتنفيذ الاختبار. استخدم الخصائص ClassInitialize/ClassCleanup و TestInitialize/TestCleanup.

 في حالتنا:

  • أضفنا ملف App.config لمشروع الاختبار(يحتوي على وصلة اتصال قاعدة البيانات وعلى نص SQL التمهيدي المعروض أدناه).
  • أضفنا إجرائية تمهيدية للاختبار  تفتح وصلة اتصال مع قاعدة البيانات وتنفذ النص البرمجي.
  • وأضفنا إجرائية التحرير التي تقطع الاتصال بقاعدة البيانات ثانيةً.

هذا هو نص  SQL:

Copy

1

begin tran;

2

3

delete from TimeTracking;

4

delete from [User];

5

delete from TaskType;

6

delete from Project;

7

8

insert into Project ( ProjectCode, ProjectDescription )

9

values ( ‘PRJ1’, ‘Project 1’ );

10

insert into Project ( ProjectCode, ProjectDescription )

11

values ( ‘PRJ2’, ‘Project 2’ );

12

insert into Project ( ProjectCode, ProjectDescription )

13

values ( ‘PRJ3’, ‘Project 3’ );

14

15

insert into TaskType ( TaskTypeCode, TaskTypeDescription )

16

values ( ‘Travel’, ‘Time used for travelling’ );

17

insert into TaskType ( TaskTypeCode, TaskTypeDescription )

18

values ( ‘Programming’, ‘Time used for programming’ );

19

insert into TaskType ( TaskTypeCode, TaskTypeDescription )

20

values ( ‘Testing’, ‘Time used for testing’ );

21

insert into TaskType ( TaskTypeCode, TaskTypeDescription )

22

values ( ‘Other’, ‘Time used for other things’ );

23

24

insert into [User] ( WindowsLogin ) values ( ‘rainer’ );

25

insert into [User] ( WindowsLogin ) values ( ‘karin’ );

26

27

commit;

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

وهذا هو النص البرمجي لتمهيد الاختبار لصنف DataAccessLayerTests:

Copy

1

#region Test initialization and cleanup code

2

private BugBustersDataContext db;

3

4

/// Initialize test database using SQL initialization script and open data context

5

[TestInitialize()]

6

public void TimeTrackingConstraintsTestInitialize()

7

{

8

    string connString = ConfigurationManager.

9

    ConnectionStrings[“TestDatabase”].ConnectionString;

10

    db = new BugBustersDataContext(ConfigurationManager.

11

    ConnectionStrings[“TestDatabase”].ConnectionString);

12

    db.Connection.Open();

13

    var cmd = new SqlCommand(ConfigurationManager.

14

    AppSettings[“TestDatabaseInitializationScript”],

15

    (SqlConnection)db.Connection);

16

    cmd.ExecuteNonQuery();

17

    db.Connection.Close();

18

}

19

20

/// Close data context after test is done

21

[TestCleanup()]

22

public void TimeTrackingConstraintsTestCleanup()

23

{

24

    db.Dispose();

25

    db = null;

26

}

27

#endregion

لاحظ كيف استخدمنا توابع الصنف Assert للتأكد من نجاح الاختبار.

بعد أن اهتممنا ببيانات الاختبار الصحيحة لقاعدة بياناتنا أصبحنا جاهزين لبناء روتين الاختبار التالي:

Copy

1

[TestMethod]

2

public void AddSingleRow()

3

{

4

    var startTime = new DateTime(2007, 1, 1, 8, 0, 0);

5

    var duration = new TimeSpan(1, 0, 0);

6

7

    var numberOfRecords = db.TimeTrackings.Count();

8

9

    var tt = new TimeTracking();

10

    tt.Project = db.Projects.First();

11

    tt.TaskType = db.TaskTypes.First();

12

    tt.User = db.Users.First();

13

    tt.StartTime = startTime;

14

    tt.EndTime = startTime + duration;

15

    tt.Comment = “This is a test case generated during unit testing!”;

16

    db.SubmitChanges();

17

18

    Assert.AreEqual(numberOfRecords + 1, db.TimeTrackings.Count());

19

}

 بطريقة مشابهة باستطاعتنا بناء إجراءات الاختبار لتحديث وحذف كيانات التتبع الزمني. ونصبح بعد ذلك جاهزين للبدء بأول اختبار وحدة. في قائمة الاختبار ضمن Visual Studio يمكنك الاختيار بين تشغيل أو تنقيح الاختبارات وفي حالتنا جميع الاختبارات الثلاثة نجحت لأن الآلية المطلوبة مزودة مسبقاً بواسطة Linq-to-SQL في مكان آخر.

المثال القسم الثالث – إضافة منطق العمل (Business Logic) 

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

استناداً إلى فلسفة TDD علينا ألا نبدأ بكتابة كود منطق العمل، ولكن علينا أولاً أن نبدأ بالاختبار!.

Copy

1

[TestMethod]

2

public void EndBeforeStart()

3

{

4

    var exception = false;

5

6

    try

7

    {

8

        // try to add a time tracking row with end < start -> must not work

9

        AddTimeTrackingRow(new DateTime(2007, 1, 1, 9, 0, 0),

10

        new TimeSpan(-1, 0, 0));

11

    }

12

    catch (Exception ex)

13

    {

14

        Assert.IsInstanceOfType(ex, typeof(ApplicationException));

15

        Assert.AreEqual(“StartTime must be lower than EndTime!”, ex.Message);

16

        exception = true;

17

    }

18

19

    Assert.IsTrue(exception);

20

    Assert.AreEqual(0, db.TimeTrackings.Count());

21

}

بالطبع الاختبار سوف يفشل لأننا لم نَبْنِ المنطق المقابل في التطبيق بعد.

لأننا استخدمنا Linq-to-SQL فإنه من السهل إضافة منطق العمل المناسب. نحن استخدمنا إجرائية الـ Linq القابلة للتوسع TimeTracking.OnValidate. وأضفنا الكود المناسب هناك:

Copy

1

using System;

2

using System.Linq;

3

using System.Data.Linq;

4

5

namespace BugBusters.Data

6

{

7

    public partial class TimeTracking

8

    {

9

        partial void OnValidate(System.Data.Linq.ChangeAction action)

10

        {

11

            if (action == ChangeAction.Insert || action == ChangeAction.Update)

12

            {

13

                if (StartTime >= EndTime)

14

                throw new ApplicationException(

15

                “StartTime must be lower than EndTime!”);

16

            }

17

        }

18

    }

19

}

الآن ينجح الاختبار!

في السطر الذي يحتوي على قاعدة العمل الأولى باستطاعتنا الآن:

  • إضافة إجرائيتي اختبار لقاعدة عمل “منع التداخل”، جربهما وتحقق من كونهما تفشلتان.
  • أضف منطق العمل المناسب، وشاهد نجاح الاختبارات.

(تابع فقط عند نجاح جميع الاختبارات، ولا تكتف بالتأكد من آخر اختبار أنجزته).

هنا نكون قد أنجزنا كامل المطلوب في الوصول إلى المعطيات ومنطق العمل إضافةً إلى مجموعة كاملة من اختبارات الوحدة بما يغطي جميع المزايا التي كتبناها.

المثال القسم الرابع – مقدمة قصيرة في الـ Wat

إن التطوير المقاد بالاختبار لعناصر واجهة المستخدم أصعب بقليل منه لطبقة الوصول للبيانات أو طبقة المعالجة. مما ذكر سابقاً فإن جميع أدوات التسجيل و الالتقاط لا تفيد طريقة التطوير هذه. WatiN هي أداة عظيمة مفتوحة المصدر من أجل الاختبار الآلي في مجال تطبيقات الويب وهي تؤمن اختبار مقاد لواجهة المستخدم.

باختصار WatiN هي تغليف لنماذج أغراض مستعرض الـ IE. وهي تؤمن مجموعة من الأصناف التي تمكنك تقريباً من محاكاة كل شيء تفعله من خلال لوحة المفاتيح أو الفأرة ضمن الـ IE بنفسك. توابع الـ API بسيطة وبديهية وحتى أنها ملائمة وعملية لكتابة اختبارات دون الحاجة لأدوات تسجيل.

ولأن WatiN تتعامل مع جميع محتويات مستعرض IE النشطة (مثل: JavaScript, ActiveX, Flash, Silverlight ………) والتي تنفذ أثناء الاختبارات  يمكن استخدام هذه الأداة من أجل الاختبارات الآلية كنصوص JavaScript . والنسخة الحالية لـ WatiN تعمل فقط مع مستعرض الـ IE وكما قرأت في عدة مقالات أن دعمها لمستعرض Firefox قريب جداً.

إن مكمن جمال الـ WatiN هو في بساطته. فلديك صنف رئيسي تستخدمه معظم الوقت هو: WatiN.Core.IE. وباستطاعتك أيضاً:

  • إنشاء مكون جديد (فتح نافذة IE جديدة)
  • أو الارتباط بنافذة موجودة – (IE.AttachToIE  (AttributeConstraint

إن WatiN.Core namespace تحتوي تقريباً على أصناف (classes) جميع عناصر الـ HTML التي يمكن أن تظهر في موقعك على الانترنت. يمكنك كشف أي عنصر (control component) باستخدام الإجرائية المناسبة في صنف (class) الـ IE. هذه بعض الأمثلة:

  • Link myLink = ie.Link( AttributeConstraint );
  •  Button myButton = ie.Button( AttributeConstraint );
  • TextField myTextField = ie.TextField( AttributeConstraint );

أنت على الأرجح لاحظت اسم الصنف AttributeConstraint في المثال السابق. ولإنشاء غرض من هذا الصنف استخدم WatiN.Core.Find class. إنها تحتوي على مجموعة من إجراءات Find.By… (مثل: ……. ById, ByName, ByTitle, ByUrl) جميعها تعيد غرض من AttributeConstraint الذي يمكن استخدامه من أجل البحث ضمن نوافذ مستعرض الـ IE (مثل: IE.AttachToIE)، وعناصر HTML (مثل: TextField).

سنناقش بعض التفاصيل مثل معالجة مربعات النص لرسائل JavaScript ومربعات حوار HTML فيما بعد. حالياً أنت لديك المعرفة الكافية لكتابة الاختبارات.

تتمة المقالة:  التطوير المقاد بالاختبار – II