عنوان

معماری لایه ای در خودکاری سازی تست نرم افزار

عنوان انگليسي

Layered Architecture for Test Automation

كلمات كليدي

Domain-Driven Design, Software Testing, Delivering Quality, Test Automation

مؤلف

Bei Li

مرجع

http://www.infoQ.com

سطح متوسط مترجم مهدي عبداللهي تاريخ انتشار

30 خرداد 1389

تعداد صفحه

9

فايل هاي ضميمه

Layered.Architecture.Code.Sample.zip (1st Link)

Layered.Architecture.Code.Sample.zip (2nd Link)

مطالب مرتبط

دانلود متن مقاله (لینک 1)

دانلود متن مقاله (لینک 2)

مقدمه ی مترجم

این مقاله را در پاسخ به درخواست یک دانشجوی نرم افزار پیدا و ترجمه نمودم. در حین ترجمه دریافتم که این موضوع بسیار گسترده تر از آنی است که فکر می کردم و مدام داخل متن اصلی مقاله به موضوعاتی برخورد کردم که هر کدام به طور جداگانه لازم است که مطالعه و بررسی شوند. از طرفی خودم تجربه ی عملی در خصوص موضوع این مقاله ندارم و همین ممکن است باعث شده باشد که برگردان برخی واژه ها برای اهل فن مأنوس نباشد. با این حال هر کدام از لینک های داخل مقاله را بهتر است جداگانه مطالعه نمایید. من نیز امیدوارم که در فرصتی مناسب مطالب مرتبط با این موضوع را ترجمه کرده، در اختیار دوستان و علاقه مندان قرار دهم.

از طرف دیگر آماده شدن این مقاله برای انتشار مصادف شد با فیلتر شدن وردپرس با حکم دادستانی تهران، که باعث شد قدری تأمل کنم و در نهایت تصمیم گرفتم که با وردپرس ادامه دهم. هر چند که اگر امکان داشته باشم یک فضای اختصاصی برای این وبلاگ خریداری خواهم کرد.

***

چکیده

در خودکار سازی تست صرفا تست منطق کد برنامه مطرح نیست بلکه مواردی مربوط به دیگر کد های استفاده شده مانند الحاق رشته ای آدرس وب (url concatenation)، تفسیر کد های html/xml، دسترسی به واسط کاربر و … نیز درگیر این جریان می شود. در واقع باید فاتحه ی تست بی دردسر منطق برنامه را بخوانیم چون با این ترکیباتی که صحبت شد تست کد بسیار پیچیده می شود. در این مقاله برای حل مشکل مذکور یک روش لایه به لایه برای خودکار سازی تست نرم افزار ارایه داده ایم. بدین ترتیب که با تقسیم بندی سه لایه ای به ترتیب:

1-     تست کیس ها که بر روی تست منطق برنامه متمرکز می شوند

2-     لایه ی بیزینس لاجیک، مدل سازی سیستم زیر تست در شرایط بیزینس لاجیک مانند کپسوله کردن درخواست های http، کنترل مرورگر، منطق تفسیر نتایج پردازش ها و کار بر روی واسط برای لایه ی تست کیس ها

3-     تست سیستم که لایه ی 2 مستقیما در آن عمل کند

مسأله

سلسله عملیات کنترل کیفیت نرم افزار شامل تست های اکتشافی و بازگشتی و … است. همان قدری که برخی مراحل از جمله تست های اکتشافی پیچیدگی خاص خودش را دارد و نیازمند هوشمندی و کار بلد بودن است از آن طرف مواردی مانند تست بازگشتی شامل یک سری کار های روتین و تکراری است. با افزودن قابلیت های نرم افزار، تست های بازگشتی زمان بیشتری را لازم دارند.

خودکار سازی تست این مشکل را حل می کند بدین معنا که کارهای تکراری مانند تست های بازگشتی توسط کامپیوتر انجام می شوند  و تست کیس ها به صورت برنامه ی کامپیوتری در می آیند. در این حال بخش کنترل کیفیت می تواند از دردسر های تست های تکراری و خسته کننده راحت شود و با در اختیار داشتن زمان بیشتر، روی موارد مهم تر تمرکز کند.

در خودکار سازی تست صرفا کد تست لاجیک مطرح نیست بلکه مواردی مربوط به دیگر کد های استفاده شده مانند الحاق رشته ای آدرس وب (url concatenation)، تجزیه ی کد های html/xml، دسترسی به واسط کاربر و … نیز درگیر این جریان می شود. به عنوان مثال برای تست یک وب سرویس که کار های متنوعی مانند جستجو بر حسب کلمه ی کلیدی و برگرداندن یک فایل xml شامل اطلاعات دقیق (مانند اطلاعات مشتری) را انجام می دهد، کد مربوط به خودکار سازی تست باید شرایط زیر را داشته باشد:

1-     یک آدرس وب (url) بر مبنای عملیات آزمایشی مورد نظر درست کند

2-     یک درخواست http را با یک سری کتابخانه ی http ارسال نماید

3-     پاسخ دریافتی از وب سرور را تفسیر و xml را تجزیه کند

4-     نتایج دریافتی را با آن چه انتظار داشته مقایسه نماید

در برخی موارد کد خودکار سازی تست، تمامی مراحل الحاق رشته ای آدرس وب (url) ، تجزیه ی html/xml عبارت XPath و تست لاجیک به صورت یکجا نوشته می شوند مثلا به صورت یک کلاس یا یک متد. این روش کاملا دم دستی و بدیهی است ولی مشکلات  خاص خودش را هم دارد:

1-     فهمیدن و تغییر تست منطق برنامه بسیار سخت و وقت گیر است. وقتی تست منطق برنامه را با مقادیر حجیم کد های مربوط به بخش های دیگر مخلوط کنیم سر نخ از دست مان در می رود. برای افزودن تست کیس جدید باید دوباره از اول همه ی کد را بخوانیم تا بهترین جا را برای اضافه کردن کد جدید پیدا کنیم. در نتیجه تست منطق برنامه رفته رفته سخت تر می شود.

2-     وقتی که تست منطق برنامه و کدهای پشتیبان مثل تجزیه ی html را با هم انجام دهیم، تست ها خیلی نامطمئن می شوند و یک تغییر کوچک در قواعد حاکم بر تست سیستم و کد خودکار سازی آن کافی است تا کار فرآیند تست را یکسره کند. مثلا اگر واسط کاربر تغییر کند و یک عنصر ورودی به یک تگ div دیگر منتقل شود یا ID یک عنصر واسط کاربر (مثل textarea و …) عوض شود، سر تا سر کد تست این مرحله را تحت تأثیر قرار می دهد.

3-     هزینه ی نگهداری افزایش می یابد چرا که عموما چند تست کیس برای یک بخش مشخص از یک سیستم وجود دارد و بخش عمده ی هر تست کیس، مشابه بقیه است.به عنوان مثال ممکن است همه ی تست کیس ها به ترتیب کار های زیر را انجام دهند:

1- یک url مطابق با عملیات آزمایشی تولید کنند

2- یک درخواست http با چند کتابخانه ی http ارسال کنند

3- پاسخ دریافتی از وب سرور را تفسیر کرده، فایل xml را تجزیه کنند

4- نتایج برگشت داده شده را با آنچه مورد انتظار است مقایسه کنند

با توجه به این که کد های مذکور باید در تمامی تست کیس ها کپی شوند، حال اگر یک تغییر کوچک در هر کدام از مراحل ایجاد شود باید تک تک تست کیس ها را نیز تغییر دهید.

راه حل

تجربیات توسعه ی نرم افزار در نهایت منجر به ابداع یک راه حل تحت عنوان «معماری لایه ای» شده است. اساسا معماری لایه ای را -که به آن «طراحی مبتنی بر حوزه» (Domain Driven Design) گفته می شود- می توانیم به طور خلاصه شرح دهیم:

فایده ی لایه ها در این است که هر کدام شان اختصاصا یک کار مشخص از یک برنامه ی کامپیوتری را انجام می دهند. این عملکرد تخصصی بخش ها این امکان را به ما می دهد که هر جنبه از برنامه را تمیز و پیوسته طراحی کنیم و در واقع راحت به هم چفت کنیم و در نهایت یک برنامه قابل فهم تر باشد.  صد البته مهم است که لایه ها را طوری انتخاب و طراحی کنیم که کار های مهم طراحی نرم افزار را انجام دهند.

برای اطلاعات دقیق تر می توانید به آدرس http://www.domaindrivendesign.org مراجعه نمایید.

با این که این روش در خودکار سازی تست متفاوت است ولی هر دوی این ها یک مشکل را حل می کنند و در نتیجه راه حل مشابهی را می توانیم برای شان پیاده کنیم:

لایه ی تست کیس تمامی تست های منطق برنامه در این لایه متمرکز هستند. تست کیس های با سناریو های متفاوت و موارد خاص به همان بخش از کد در لایه ی پایین تر مربوط هستند و تنها تفاوت شان در پارامتر یا داده های آزمایشی مورد استفاده شان است.
لایه ی بیزینس لاجیک این لایه عملیات را برای سیستم در حال تست (لایه ی پایین تر از خودش) کپسوله می کند مانند ساختن (الحاق رشته ای) url، تجزیه ی html/xml، واسط کاربر سمت کلاینت، کنترل مرورگر و … . در واقع سیستم در حال تست را به زبان بیزینس لاجیک بیان می کند.
لایه ی سیستم در حال تست سیستم به طور کلی تست می شود

لایه ی تست کیس شامل موارد تست متعددی می باشد. این تست کیس ها بر مبنای لایه ی بیزینس لاجیک پایه گذاری شده اند که سیستم در حال تست را کپسوله سازی می کند. لایه ی بیزینس لاجیک به طور مستقیم به لایه ی سیستم در حال تست دسترسی دارد.

مثال

فرض کنید یک وب سرویس را می خواهیم تست کنیم که به وسیله ی آن اطلاعات مربوط به یک مشتری را با دادن شماره ی تلفن وی به دست می آورید. برای فراخوانی این وب سرویس درخواست get باید به شکل زیر به وب سرویس ارسال شود:

http://{endpoint}/subscribers?telephoneNumber={telephoneNumber}

داده های خروجی شامل نام، شماره ی تلفن، نشانی و … هستند:

13120205504|ST|C|SQ|112|||FIRST|ST|W|Riverfront|BC|010|68930432|

تست کیس های مربوط به این سرویس عبارت اند از :

  1. جستجوی یک شماره تلفن که به صورت کامل داده شده است و فقط یک انطباق (رکورد) برای آن موجود است.
  2. جستجوی یک شماره تلفن که به صورت کامل داده شده است و بیش از یک انطباق (رکورد) برای آن موجود است.
  3. جستجوی یک شماره تلفن که بخشی از آن (و نه به طور کامل) داده شده است و بیش از یک انطباق (رکورد) برای آن موجود است.

تعداد تست کیس ها بستگی به درک و خلاقیت مهندس بخش کنترل کیفیت نرم افزار دارد.

برای هر کدام از تست کیس های بالا پردازش های مشابهی انجام می شود، یعنی:

  1. یک آدرس وب (url) ساخته می شود که شماره ی تلفن به صورت کلمه ی کلیدی در آن قرار دارد.
  2. از طریق کتابخانه ی http یک درخواست ارسال می شود.
  3. اطلاعات خروجی تجزیه می شوند.
  4. داده های دریافتی با آنچه انتظارش می رفت مقایسه می شوند.

حال برای پیش گیری از بروز مشکلاتی که گفتیم، روش لایه به لایه را به کار می بندیم.


لایه ی تست کیس

پیاده سازی این لایه به فریم ورک وابستگی زیادی دارد. در این مثال ما از زبان C# و فریم ورک NBehave استفاده کرده ایم. (ر.ک www.nbehave.org)

[Story]

public class SearchCustomerbyTelephoneNumberStory: TestBase

{

[Scenario]

public void SearchWithAPhoneNumberWhichHasAnExactMatch()

{

story.WithScenario(«Search with a phone number which has a exact match»)

.Given(AN_ACCOUNT_WITH_PHONE_NUMBER, «01068930432», EMPTY_ACTION)

.When(SEARCH_WITH, «01068930432»,

SEARCH_WITH_ACTION)

.Then(ACCOUNT_INFORMATION_SHOULD_BE_RETURNED, «13120205504»,

ACCOUNT_INFORMATION_SHOULD_BE_RETURNED_ACTION)

.Given(AN_ACCOUNT_WITH_PHONE_NUMBER, «01062736745»)

.When(SEARCH_WITH, «01062736745»)

.Then(ACCOUNT_INFORMATION_SHOULD_BE_RETURNED, «12666056628»);

}

[Scenario]

public void SearchWithPartialPhoneNumber()

{

story.WithScenario(«Search with partial phone number»)

.Given(THREE_ACCOUNTS_WITH_PHONE_NUMBER_STARTS_WITH, «0106», EMPTY_ACTION)

.When(SEARCH_WITH, «0106», SEARCH_WITH_ACTION)

.Then(ACCOUNT_INFORMATION_SHOULD_BE_RETURNED, «13120205504»,

ACCOUNT_INFORMATION_SHOULD_BE_RETURNED_ACTION)

.And(ACCOUNT_INFORMATION_SHOULD_BE_RETURNED, «12666056628»)

.And(ACCOUNT_INFORMATION_SHOULD_BE_RETURNED, «17948552843»);

}

[Scenario]

public void SearchWithAPhoneNumberWhichHasSeveralExactMatches() {…}

[Scenario]

public void SearchWithNonExistentPhoneNumbers() {…}

[Scenario]

public void SearchWithInvalidPhoneNumberValues() {…}

}

تست کیس ها در C# نوشته شده اند ولی به زبان انگلیسی (زبان آدمیزاد) نزدیک تر اند.

(ر.ک http://www.martinfowler.com/bliki/BusinessReadableDSL.html )

با این روش بقیه ی رل ها که در لایه ی بیزینس به چشم نمی آیند، در نظر گرفته می شوند. این حالت برای فریم ورک هایی که فقط تست کیس های متنی را پشتیبانی می کنند (مانند Cucumber – ر.ک http://www.cukes.info) مفید است.

متغیر های با پسوند ACTION عبارت های لاندا (lambda expressions- ر.ک msdn.microsoft.com/en-us/library/bb397687.aspx) هستند.

SEARCH_WITH_ACTION برای ارسال درخواست به وب سرویس و تجزیه ی داده های برگشتی است. کد مربوط به CustomerService و Subscriber در لایه ی بیزینس لاجیک است، چون این کد در تست کیس های مختلف، مشترک است.

SEARCH_WITH_ACTION =

phoneNumber =>

{

subscribers = customerService.SearchWithTelephoneNumber(phoneNumber);

};

ACCOUNT_INFORMATION_SHOULD_BE_RETURNED_ACTION برای بررسی داده ها استفاده می شود:

ACCOUNT_INFORMATION_SHOULD_BE_RETURNED_ACTION =

accountNumber =>

{

//Get expected subscriber from fixture

Subscriber expected = SubscriberFixture.Get(accountNumber);

CustomAssert.Contains(expected, subscribers);

};

لایه ی بیزینس لاجیک

کلاس CustomerService بعد از نام واقعی این وب سرویس نام گذاری شده است. در این مستندات مربوط به نیازمندی های برنامه، مدل معماری نرم افزار، و کد برنامه این وب سرویس به همین نام ذکر شده است. (برای جزئیات بیشتر به مطلب با عنوان Domain Based Testing در آدرس http://libei.net/2009/07/10/domain-based-testing مراجعه نمایید. مترجم)

public class CustomerService

{

public Subscriber SearchWithTelephoneNumber(string telephoneNumber)

{

string url =

string.Format(

«{0}/subscribers?telephoneNumber={1}»,

endpoint, telephoneNumber);

//Send http request to web service, parse the xml returned,

//populate the subscriber object and etc.

return GetResponse(url);

}

}

کلاس Subscriber اطلاعات مشتری را مدل می کند. در مقایسه با قالب رشته ای متصل به هم – که در روش تست قبلی دیدیم- این قالب محسوس تر و قابل فهم تر است چون در روش قبلی برای استخراج شماره ی تلفن باید از عبارت pipedData[101] استفاده می کردید.

public class Subscriber

{

public string AccountNumber { get; set; }

public string FirstName { get; set; }

public string Surname { get; set; }

public string TelephoneNumber { get; set; }

}

با این روش مدل سازی، اعتبار سنجی داده ها به صورت شیء گرا انجام می شود. مثلا برای بررسی این که نام مشتری Ronald است، کافی است دستور زیر را استفاده نمایید:

Assert.AreEqual(«Ronald», subscriber.FirstName);

یا این که یک شماره ی تلفن با 021 شروع می شود:

Assert.IsTrue(subscriber.TelephoneNumber.StartsWith(«021»));

سورس کد مربوط به خودکار سازی تست را که در این مقاله صحبت کردیم، می توانید در ضمیمه ی همین مقاله ببینید. از طریق محیط ویژوال استودیو 2008 یا خط فرمان می توانید آن را اجرا نمایید. می توانید فایل go.bat را اجرا نمایید تا تست را انجام دهد و نتایج تست را در پوشه ی artifacts ببینید. سورس کد این سولوشن شامل سه پروژه است. پروژه ی با پسوند نام Client مربوط به لایه ی تست بیزینس لاجیک است. پروژه ی با پسوند Client.spec (spec مخفف specifications است) تست مربوط به توسعه ی این لایه را در بر دارد (با TDD). و پروژه ی Stories همان لایه ی تست کیس ها است. این سورس کد از یک پروژه ی واقعی گرفته شده است و شما پوشه هایی در آن می بینید که صرفا یک فایل دارند. این پوشه ها استفاده نمی شوند ولی پوشه هایی که بیش از یک فایل داخل شان هست لازم اند. . به علاوه کلاس هایی هستند که مقدار برگشتی آن ها کد گذاری شده است و دلیل این هم جدا سازی این پروژه از سیستم واقعی است.


این روش چگونه مشکل ما را حل می کند؟

مشکل: فهمیدن و دستکاری تست لاجیک سخت است. از آن جا که ما یک لایه ی جداگانه برای تست لاجیک داریم و کد مورد نیاز مان را از لایه ی پایین تر استفاده می نماییم، تست کیس ها را می توانیم به زبان مشابه انگلیسی بنویسیم. در این حالت دستکاری و فهمیدن کد به مهارت کد نویس در زبان انگلیسی بستگی خواهد داشت.

مشکل: تست ها بسیار آسیب پذیر اند. با وجود لایه ی بیزینس لاجیک – که تست کیس ها را از سیستم واقعی در حال تست جدا می کند- در تغییری در سیستم فقط به این لایه سرایت می کند. اگر کد های این لایه را تغییر دهیم، تست کیس های این لایه هنوز می توانند اجرا شوند.

مشکل: کد نگهداری پر حجم است. با کپسوله سازی در لایه ی بیزینس لاجیک، کد های تکراری از تست کیس ها حذف شده اند و به هنگام تغییر فقط یک بخش از کد را باید دستکاری کنید. از آن جا که  سرویس ها و مدل های بیزینس لاجیک، سیستم در حال تست را مدل می کنند، فهمیدن و دستکاری کد آسان می شود.

پرسش و پاسخ

پرسش: این روش به نظر پیچیده است. آیا حتما از این روش باید استفاده کنیم؟

پاسخ: بستگی به پیچیدگی و اندازه ی سیستم در حال تست دارد. اگر سیستم خیلی کوچک باشد و لایه ی بیزینس لاجیک به اندازه ی کافی ساده باشد این روش به درد نمی خورد. در این حالت خودکار سازی تست بیشتر اتلاف وقت است تا کار. با چند دقیقه زمان به همان روش سنتی می توانید برنامه را تست کنید. برای پروژه های متوسط بهتر است که از ترکیب تست و کد پشتیبان استفاده کنید. در نهایت پروژه هایی که لایه یبیزینس لاجیک آن ها پیچیده است باید از تست لایه به لایه استفاده کنید.

پرسش: این معماری لایه ای ساز و کار خاص خود را پیش از آغاز تست های واقعی لازم دارد. این کار هزینه بر نیست؟

پاسخ: این صرفا یک روش سازمان دهی کد است. اگر کد تست مرتب سازی نشود باز هم کد های مربوط به الحاق رشته ای آدرس وب (url) ، تجزیه ی مقادیر برگشتی html/xml و بررسی درستی نتایج را باید بنویسیم. با این معماری شما صرفا کد را به کلاس ها یا متدهای مجزا تقسیم می کنید. به علاوه نیاز ندارید که این لایه ها را به طور کامل پیاده سازی کنید. این لایه ها بر مبنای سناریو (scenario-driven) یا بر مبنای تست کیس هستند و در موقع لزوم باید پیاده سازی شوند.

پرسش: این روش طراحی مهارت شیءگرایی می خواهد که همه ی مهندسان کنترل کیفیت نرم افزار این مهارت را ندارند.

پاسخ: خودکار سازی تست صرفا مربوط به بخش کنترل کیفیت نیست و اعضای دیگر تیم مانند توسعه دهنده گان و … نیز باید در آن همکاری کنند. توسعه دهنده گان در بخش بیزینس لاجیک، نوشتن کد های پشتیبان و ایجاد یک پلتفرم تست منطبق با پروژه ی در دست اجرا برای بخش کنترل کیفیت همکاری می کنند و بخش کنترل کیفیت، تست کیس ها را طراحی می کند و صرفا در لایه ی تست کیس ها کد می نویسد.

(ر.ک http://libei.net/2009/07/07/test-automation-shared-responsibility-between-qa-and-developers )