تست Unit در Xcode — راهنمای مقدماتی
ساخت نرمافزار فرایند پیچیدهای است. علاوه بر مهارت در کار با ابزارهای مختلف و زبانهای برنامهنویسی، باید با نقشهای مختلف در زمینه توسعه نرمافزار نیز آشنا باشید. همچنان که میدانیم پروژههای بزرگ صرفاً شامل کدنویسی نیستند. پروژههای بزرگ به منابع دیگری جهت گردآوری الزامات، ساخت پروتوتایپ و تست کردن هم نیاز دارند. در این مقاله با فرایند تست Unit در Xcode آشنا خواهیم شد.
فرایند اعتبارسنجی کد در اغلب موارد به نام «تضمین کیفیت» (Quality Assurance) نامیده میشود. با این حال، این اصطلاح تا حدی گمراه کننده است. در واقع هدف ما به جای اطمینان یافتن از کیفیت کد، در اغلب موارد با تکمیل یافتن بیشتر پروژه، هدف اصلی ما اندازهگیری آن است. این فرایند که به نام «توسعه تست-محور» (test-driven development) یا به اختصار TDD شناخته میشود، تفاوت ظریفی دارد که در زمان بررسی نقشهای کارکرد کیفیت آن را مشاهده میکنیم. این تفاوتها به صورت زیر هستند:
تستهای Unit به چه معنا هستند؟
چنان که اشاره کردیم، اندازهگیری کیفیت در اغلب موارد به صورت فعالیتهای خودکار و دستی تقسیمبندی میشود. با توجه به علاقه ما به الگوریتمها، از کدهای اضافی به نام «تستهای واحد» (Unit Test) برای تست پروژه نرمافزاری استفاده میکنیم. تستهای Unit به صورت معمول از سوی توسعهدهندگان نوشته میشوند و نقطه تمرکز محیط TDD محسوب میشوند. در ادامه با استفاده از فریمورک XCTest در iOS به بررسی شیوه اجرای تست واحد در سوئیفت میپردازیم.
کار با XCTest
اگر با مفاهیم سوئیفت آشنا باشید، درک شیوه نوشتن تستهای Unit نباید دشوار باشد. چنان که اشاره کردیم تستهای Unit برای بررسی کدی استفاده میشوند که فاقد اینترفیس کاربر نهایی است. برای روشنتر شدن موضوع یک حالت تست را مورد بررسی قرار میدهیم که در آن از ساختمان داده پشته (Stack) استفاده شده است.
با این که در این مقاله قصد نداریم ویژگیهای خاص IDE Xcode را بررسی کنیم، اما باید اشاره کنیم که تستها با استفاده از تابع، کلاس یا target قابل اجرا هستند. در زمان استفاده از Xcode تستهای واحد با اضافه کردن یک عبارت Test Target به پروژه کد اصلی ایجاد میشوند. زمانی که تستهای واحد را پیکربندی کنید، میتوانید آنها را از IDE یا خط فرمان اجرا کنید.
قواعد تست
با این که الزامی نیست اما همواره بهتر است که فایل تست Unit تطبیق زیادی با روش نامگذاری فایل-(های) پیادهسازی داشته باشد. در این مورد ما از نام StackTest.swift استفاده میکنیم. برای کسب دسترسی به متدهای اصلی پشته و مشخصههای پروژه اصلی، فایل شامل یک گزاره ایمپورت testable نیز شده است:
1import XCTest
2@testable import SwiftStructures
3class StackTest: XCTestCase {
4//called before each test method in the class
5 override func setUp() {
6 super.setUp()
7 }
8
9 //example of a functional test case
10 func testExample() {
11
12 }
13}
14...
XCTest نیز مانند دیگر فریمورکهای تست Unit از طریق ادغام ویژگیهای زبان سوئیفت با تابعهای خاص مرتبط با تست عمل میکند. متدهای اصلی به صورت assertion با هم گروهبندی شدهاند. زمانی که تستها را ایجاد میکنیم، کامپایلر تستهای Unit را از روی تابعهایی که دارای پیشوند test هستند تشخیص میدهد.
برخلاف تابعهای معمولی سوئیفت، تستهای واحد عامدانه طوری طراحی شدهاند که واحدهای منطقی را به صورت مستقل (self-contained) در خود داشته باشند. در نتیجه متدهای تست آرگومان نمیپذیرند و مقدار بازگشت نمیدهند. به طور جایگزین دادههای تست میتوانند از طریق متدهای کمکی و مقداردهی یا تقسیم کردن دنبالهها مدیریت شوند. به مثال زیر توجه کنید:
1class StackTest: XCTestCase {
2var numberList: Array<Int>!
3
4 override func setUp() {
5 super.setUp()
6 numberList = [8, 2, 10, 9, 7, 5]
7 }
8func testPushStack() {
9
10 let myStack = Stack<Int>()
11 XCTAssertTrue(myStack.count == 0, “test failed: count not initialized..”)
12
13 //build stack
14 for s in numberList {
15 myStack.push(s)
16 print(“item: \(s) added..”)
17 }
18 XCTAssertEqual(myStack.count == numberList.count, “stack count does not match..”)
19
20} //end class
برنامهریزی تست
عملیات اصلی ساختمان داده پشته، افزودن و حذف آیتمهای داده است. به جای نوشتن یک تابع منفرد برای تست همه عملیات، برنامه تست ما این است که هر عملیات اصلی پشته را با تست خاص خودش مجزا سازیم. چنان که در testPushStack دیدیم، یک assertion به نام XCTAssertTrue مقداردهی صحیح متغیر Stack.count را بررسی میکند. گام بعدی شامل اجرای Stack.push از طریق تعریف حلقه تکرار روی آرایه آیتمهای numberList است. برای تأیید این که هر تست از دادههای یکسانی استفاده میکند، numberList با استفاده از متد راهاندازی کلاس XCTestCase مقداردهی میشود.
اینک با پیادهسازی تست واحد برای افزودن آیتمهای پشته، میتوانیم تست بعدی را برای حذف کردن آیتمها بنویسیم:
1func testPopStack() {
2
3 //build stack - helper function
4 let myStack: Stack<Int> = self.buildStack()
5
6 if myStack.count == 0 {
7 XCTFail(“test failed: no stack items available..”)
8 }
9
10 //remove stack items..
11 while myStack.count > 0 {
12 print(“stack count: \(myStack.count)”)
13 myStack.pop()
14 }
15
16 XCTAssertTrue(myStack.isEmpty(), “test failed: stack structured not emptied..”)
17
18 }
بدین ترتیب به پایان این مقاله میرسیم.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای برنامهنویسی
- آموزش برنامه نویسی Swift (سوئیفت) برای برنامه نویسی iOS
- مجموعه آموزشهای دروس علوم و مهندسی کامپیوتر
- آموزش سوئیفت (Swift) — مجموعه مقالات مجله فرادرس
- ساخت الگوریتم بازگشتی در سوئیفت — از صفر تا صد
==