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

آموزش تست unit و تست UI در iOS- بخش دوم


پیش از شروع این مقاله، لازم است شما قبلاً بخش اول آموزش تست unit و تست UI در iOS را مطالعه کرده باشید. در جلسه قبل مبحت تست های غیرهمزمان را تشریح کردیم.

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

بسیاری از اپ ها با سیستم یا کتابخانه اشیاء تعامل دارند - اشیائی که شما آن ها را کنترل نمی کنید- و تستهایی که با این اشیاء تعامل دارند، می توانند کند و  غیرقابل تکرار باشند ، که این موضوع می تواند نقض کننده دو اصل FIRST که در مقالات قبل مطرح کردیم، باشد. برای حل مشکل، شما می توانید تعاملات را با دریافت ورودی از stubها یا به روز رسانی اشیاء mock ، جعل کنید.

زمانی که کد شما یک وابستگی به سیستم یا کتابخانه اشیاء دارد، فرآیند جعل را به کار می برید- ایجاد یک شیء جعلی برای بازی با آن بخش و تزریق این جعل به کد خود. Jon Reid تزریق وابستگی را به چندین روش شرح می دهد که در ادامه به تشریح این موارد پرداخته می شود.

ورودی جعلی از Stub

در این تست، شما بررسی می کنید که متد updateSearchResults(_:) اپ ، به درستی داده دانلود شده توسط session را ، parse می کند. SUT کنترل کننده view  است و شما session را با stubها و برخی از داده های از پیش دانلود شده، جعل خواهید کرد.

New Unit Test Target را از منوی + انتخاب کرده و آن را HalfTunesFakeTests بنامید. اپ HalfTunes را درست زیر عبارت import وارد کنید.

@testable import HalfTunes

 اعلان SUT، ایجاد آن در تابع setup و انتشار آن در tearDown:

var controllerUnderTest: SearchViewController!

override func setUp() {
  super.setUp()
  controllerUnderTest = UIStoryboard(name: "Main", 
      bundle: nil).instantiateInitialViewController() as! SearchViewController!
}

override func tearDown() {
  controllerUnderTest = nil
  super.tearDown()
}

نکته: SUT کنترلر view می باشد ، زیرا HalfTunes یک مشکل کنترلر view بزرگ دارد – تمام کارها در SearchViewController.swift انجام می شود. انتقال مد شبکه به ماژولهای جداگانه این مشکل را کاهش می دهد و هم چنین تست را ساده تر می کند.

در ادامه، شما به برخی داده های نمونه JSON نیاز خواهید داشت که session جعلی شما آنها را برای تست ارائه خواهد کرد. فقط به چند مورد داده JSON نیاز است، بنابراین برای محدود کردن نتایج دانلود خود از iTunes، مقدار &limit=3 را به رشته URL الحاق کنید.
https://itunes.apple.com/search?media=music&entity=song&term=abba&limit=3

این URL را کپی کرده و در مرورگر خود Paste کنید. این کار فایلی را با نام 1.txt یا مشابه آن را دانلود می کند. برای اطمینان از این که فایل دانلود شده، JSON است، آن را بازبینی کنید، سپس نام آن را به abbaData.json تغییر داده و فایل را گروه HaltTunesFakeTests اضافه کنید.

پروژه HalfTunes حاوی فایل پشتیبانی شده DHURLSessionMock.swift است. این فایل یک پروتکل ساده با نام DHURLSession با متدهای (stubs) برای ایجاد یک task داده با یک URL یا یک URLRequest را تعریف می کند. هم چنین URLSessionMock را مطابق با این پروتکل تعریف می کند که به شما اجازه می دهد با واسط های اولیه یک شیء mock URLSession  را با انتخاب داده ها، پاسخ­ها و خطاها ایجاد کنید.

بعد از عبارتی که SUT ایجاد می کند، داده و پاسخ جعلی را تنظیم کنید و شیء session جعلی را در setup()  ایجاد کنید.

let testBundle = Bundle(for: type(of: self))
let path = testBundle.path(forResource: "abbaData", ofType: "json")
let data = try? Data(contentsOf: URL(fileURLWithPath: path!), options: .alwaysMapped)

let url = URL(string: "https://itunes.apple.com/search?media=music&entity=song&term=abba")
let urlResponse = HTTPURLResponse(url: url!, statusCode: 200, httpVersion: nil, headerFields: nil)

let sessionMock = URLSessionMock(data: data, response: urlResponse, error: nil)

در انتهای setup()، session جعلی را به اپ به عنوان ویژگی از SUT اعمال کنید.

controllerUnderTest.defaultSession = sessionMock

اکنون شما آماده نوشتن تستی هستید که بررسی می کند که آیا فرخوانی updateSearchResults(_:) داده جعلی را تجزیه می کند. testExample() را با موارد زیر جایگزین کنید:

// Fake URLSession with DHURLSession protocol and stubs
func test_UpdateSearchResults_ParsesData() {
  // given
  let promise = expectation(description: "Status code: 200")
  
  // when
  XCTAssertEqual(controllerUnderTest?.searchResults.count, 0, "searchResults should be empty before the data task runs")
  let url = URL(string: "https://itunes.apple.com/search?media=music&entity=song&term=abba")
  let dataTask = controllerUnderTest?.defaultSession.dataTask(with: url!) {
    data, response, error in
    // if HTTP request is successful, call updateSearchResults(_:) which parses the response data into Tracks
    if let error = error {
      print(error.localizedDescription)
    } else if let httpResponse = response as? HTTPURLResponse {
      if httpResponse.statusCode == 200 {
        promise.fulfill()
        self.controllerUnderTest?.updateSearchResults(data)
      }
    }
  }
  dataTask?.resume()
  waitForExpectations(timeout: 5, handler: nil)
  
  // then
  XCTAssertEqual(controllerUnderTest?.searchResults.count, 3, "Didn't parse 3 items from fake response")
}

شما باید این تست را به عنوان یک تست ناهمزمان بنویسید، زیرا stub خواستار این است که به عنوان یک متد غیرهمزمان عمل کند.

ادعا می شود که searchResults قبل از اجرای task داده، خالی است – این ادعا باید درست باشد، زیرا شما یک SUT کاملاً جدید در setup() ایجاد کردید.

داده های جعلی حاوی JSON برای سه شیء آهنگ هستند، بنابراین ادعا این است که آرایه searchResults در کنترلر view شامل سه مورد است.

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

به روزرسانی جعلی برای شیء Mock

تست قبلی یک stub را برای تهیه ورودی از یک شیء جعلی به کار برد. در ادامه، شما از یک شیء Mock برای تست این که کد شما به درستی UserDefaults را به روز رسانی می کند، استفاده خواهید کرد.

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

تست بعدی شما بررسی خواهد کرد که آیا اپ به طور صحیح پیش فرض gameStyle کاربر را به روز رسانی می کند.

در ناوبر تست، روی گزینه New Unit Test Target… کلیک کرده و نام آن را BullsEyeMockTests بگذارید. موارد ذیل را درست زیر عبارت import اضافه کنید:

@testable import BullsEye

class MockUserDefaults: UserDefaults {
  var gameStyleChanged = 0
  override func set(_ value: Int, forKey defaultName: String) {
    if defaultName == "gameStyle" {
      gameStyleChanged += 1
    }
  }
}

MockUserDefaults متد:

set(_:forKey:)

را برای افزایش فلگ gameStyleChanged لغو می کند.

SUT و شیء mock را در BullsEyeMockTests اعلان کنید.

var controllerUnderTest: ViewController!
var mockUserDefaults: MockUserDefaults!

در تابع setup()، SUT و شیء mock را ایجاد کنید، سپس، شیء Mock  را به عنوان ویژگی SUT اعمال کنید.

controllerUnderTest = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as! ViewController!
mockUserDefaults = MockUserDefaults(suiteName: "testing")!
controllerUnderTest.defaults = mockUserDefaults

SUT و شیء mock را در tearDown() انتشار دهید.

controllerUnderTest = nil
mockUserDefaults = nil

تابع textExample() را با مورد زیر جایگزین کنید:

// Mock to test interaction with UserDefaults
func testGameStyleCanBeChanged() {
  // given
  let segmentedControl = UISegmentedControl()
  
  // when
  XCTAssertEqual(mockUserDefaults.gameStyleChanged, 0, "gameStyleChanged should be 0 before sendActions")
  segmentedControl.addTarget(controllerUnderTest, 
      action: #selector(ViewController.chooseGameStyle(_:)), for: .valueChanged)
  segmentedControl.sendActions(for: .valueChanged)
  
  // then
  XCTAssertEqual(mockUserDefaults.gameStyleChanged, 1, "gameStyle user default wasn't changed")
}

ادعا می شود که فلگ gameStyleChanged،  قبل از این که متد، کنترل جداگانه را انتخاب کند، برابر صفر است،. لذا اگر ادعا پس از آن درست باشد، به معنی این است که set(_:forKey:) دقیقا یکبار فراخوانی شده است.

تست را اجرا کنید. اجرای تست باید موفقیت آمیز باشد.

 

 

تست UI در Xcode

در Xcode 7 تست UI معرفی شده است، که به شما اجازه می دهد یک تست UI را با ضبط نمودن تعاملات انجام شده با UI ایجاد کنید. تست UI با پیدا کردن اشیاء UI اپ با پرس و جوها، ترکیب رویدادها و سپس ارسال آنها به آن اشیاء، کار می کند. API شما را قادر می سازد تا خواص و وضعیت اشیاء UI را بررسی کنید تا آنها را با وضعیت موردانتظار خود مقایسه کنید.

در ناوبر تست پروژه BullsEye، یک UI Test Target جدید اضافه کنید. بررسی کنید که Target که باید تست شود، BullsEye باشد. سپس نام پیش فرض BullsEyeUITests را بپذیرید.

این ویژگی را به کلاس BullsEyeUITests اضافه کنید:

var app: XCUIApplication!

در تابع setup()، عبارت  XCUIApplication().launch() را با عبارت زیر جایگزین کنید:

app = XCUIApplication()
app.launch()

نام testExample() را به testGameStyleSwitch() تغییر دهید.

یک خط جدید در testGameStyleSwitch() ایجاد کرده و روی کلید Record در زیر پنجره وایریشگر کلیک کنید.

زمانی که اپ در شبیه ساز نمایان می شود، بخش Slide از کلید سبک بازی و برچسب بالا را ضربه بزنید. سپس دکمه ضبط Xcode را برای توقف ضبط کلیک کنید.

اکنون شما سه خط زیر را در testGameStyleSwitch() دارید:

let app = XCUIApplication()
app.buttons["Slide"].tap()
app.staticTexts["Get as close as you can to: "].tap()

اگر هر عبارت دیگری باشد، آنها را حذف کنید.

خط 1 ویژگی را که شما در setup() ایجاد کردید را کپی می کند و نیازی ندارید که شما دیگر چیزی را ضربه بزنید، از این رو خط اول را حذف کرده و عبارت .tap() را از انتهای خط 2و 3 را حذف کنید. منوی کوچکی را که در کنار [“Slide”] هست، باز کنید. گزینه segmentedControls.buttons["Slide"] را انتخاب کنید.

بنابراین آن چه که شما دارید، این است:

app.segmentedControls.buttons["Slide"]
app.staticTexts["Get as close as you can to: "]

این قسمت را برای ایجاد یک بخش given، تغییر دهید:

// given
let slideButton = app.segmentedControls.buttons["Slide"]
let typeButton = app.segmentedControls.buttons["Type"]
let slideLabel = app.staticTexts["Get as close as you can to: "]
let typeLabel = app.staticTexts["Guess where the slider is: "]

اکنون که شما نام های دو کلید و دو برچسب قابل انتخاب را دارید، مورد زیر را اضافه کنید:

// then
if slideButton.isSelected {
  XCTAssertTrue(slideLabel.exists)
  XCTAssertFalse(typeLabel.exists)
  
  typeButton.tap()
  XCTAssertTrue(typeLabel.exists)
  XCTAssertFalse(slideLabel.exists)
} else if typeButton.isSelected {
  XCTAssertTrue(typeLabel.exists)
  XCTAssertFalse(slideLabel.exists)
  
  slideButton.tap()
  XCTAssertTrue(slideLabel.exists)
  XCTAssertFalse(typeLabel.exists)
}

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

تست عملکرد:

برگرفته از مستندات اپل: تست عملکرد بلوکی از کد را که شما می خواهید مورد ارزیابی قرار دهید، گرفته و آن را ده بار اجرا می کند، میانگین زمان اجرا و انحراف از معیار استاندارد را جمع آوری می کند. میانگین این اندازه گیری های تکی، یک ارزشی را برای اجرای تست تشکیل می دهد که می تواند بعداً در مقایسه با یک معیار ارزیابی شکست یا موفقیت، مورد ارزیابی قرار گیرد.
نوشتن تست عملکرد بسیار ساده است. کافی است کدی را که می خواهید ارزیابی شود، داخل متد measure() قرار دهید.
برای بررسی عملی این مطلب، پروژه HalfTunes را مجدداً باز کنید، در HalfTunesFakeTests، مقدار testPerformanceExample() را با تست زیر جایگزین کنید:
 

// Performance 
func test_StartDownload_Performance() {
  let track = Track(name: "Waterloo", artist: "ABBA", 
      previewUrl: "http://a821.phobos.apple.com/us/r30/Music/d7/ba/ce/mzm.vsyjlsff.aac.p.m4a")
  measure {
    self.controllerUnderTest?.startDownload(track)
  }
}


تست را اجرا کنید، سپس برای دیدن آمار، بر روی آیکونی که در انتهای بخش measure() نمایان است، کلیک کنید.



روی Set Baseline کلیک کنید، سپس مجدد تست عملکرد را اجرا کنید و نتیجه را مشاهده نمایید – نتیجه باید بهتر یا بدتر از مقدار Baseline باشد. کلید Edit به شما اجازه می دهد مقدار Baseline را برای نتایج جدید، reset کنید.
مقادیر Baselineها بر اساس پیکربندی هر دستگاه، ذخیره می شود، بنابراین شما می توانید همین آزمایش را روی چندین دستگاه مختلف انجام دهید.
هر زمان که شما یک اپ را تغییر می دهید که ممکن است بر عملکرد متد تست شده، تأثیر بگذارد، تست عملکرد را دوباره اجرا کنید.

Code Coverage

ابزار Code Coverage به شما می گوید که کدام کدهای اپ در تست های شما مورد اجرا قرار گرفته اند. بنابراین شما می دانید که چه بخشهایی از کد اپ شما هنوز تست تشده است.
برای فعال کردن code coverage، طرح Test action را ویرایش کرده و تیک code coverage را بگذارید.



تمام تست های خود را اجرا کنید (Command-U)، سپس ناوبر گزارش (Comman-8) را باز کنید گزینه By Time را انتخاب کنید، اولین گزینه لیست را انتخاب کرده و سپس Coverage tab را انتخاب کنید.




برای دیدن لیست توابع در SearchViewController.swift، روی مثلث disclosure کلیک کنید.



ماوس را روی نوار Coverage کنار updateSearchResults(_:) بکشید، تا ببینید که coverage برابر با 71.88% است.
برای باز کردن فایل سورس، روی کلید فلش این تابع کلیک کنید، سپس تابع را پیدا کنید. همان طور که ماوس را بر روی حاشیه نویسی coverage در نوار کناری سمت راست می کشید، قسمت های کد سبز یا قرمز برجسته می شوند.



حاشیه نویسی coverage نشان می دهد که چند بار یک تست، هر بخش کد را هدف قرار می دهد؛ بخش هایی که فراخوانی نشده اند، به رنگ قرمز برجسته می شوند. همان طور که انتظار می رفت، برای حلقه loop که سه بار اجرا شده، در مسیرهای خطا، هیچ چیزی اجرا نشده است. برای افزایش پوشش این تابع، شما می توانید abbaData.json را تکثیر کرده، سپس آن را ویرایش کنید، این امر باعث بروز خطاهای مختلفی می شود –برای مثال، تغییر “results” به “result” برای یک تستی که  print("Results key not found in dictionary") را هدف قرار می دهد.

شما اکنون ابزارهای خوبی را برای استفاده در تست های نوشتاری، در پروژه های خود دارید. امیدواریم که آموزش تست Unit و تست UI در iOS به شما این اطمینان را داده باشد که همه چیز را تست کنید!

می توانید پروژه های تکمیل شده را در این فایل زیپ مشاهده نمایید: zip file
 

فصلِ: 3 , تعداد قسمت ها: 179 , سطح: صفر تا صد

این فصل در یک نگاه:

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

این فصل در یک نگاه:

در این فصل:-نمایش notification به کاربر زمانی که اتفاقاتی از قبیل لایک کردن پست و ... میافتد(این مبحث مبحثی مهم بوده و ج…

  توضیحات کلی دوره: با سلام خوش اومدین به حرفه ای ترین دوره ی Swift ابتدای کار ممکن است سوالی برای شما پیش آید: -من نیازی به ساخت اپلیکیشن اجتماعی ندارم چرا باید این دوره را ببینم؟ ج…
فصلِ: 7 , تعداد قسمت ها: 159 , سطح: صفر تا صد
موضوعات: آموزش IOS

این فصل در یک نگاه:

اپل نویس حرفه ای شوید... با این پکیج کسب درآمد میلیونی کنید....   در این پکیج به نکات ریز و درشت زیادی پرداختیم که خیلی خیلی میتونه به شما کمک کنه که تبدیل به برنامه نویس حرفه ای شوید... …
فصلِ: 12 , تعداد قسمت ها: 126 , سطح: صفر تا صد
موضوعات: آموزش IOS

این فصل در یک نگاه:

فصل 16 فصل آخرمون هست و میایم توی این فصل یه سری مباحث کوچیکی که جامونده و کارهای نهایی برای انتشار برنامه توی مایکت های…

با سلام نکته:هیچ نیازی به mac و یا iphone نیست... نکته:هیچ نیازی به بلد بودن برنامه نویسی از قبل نیست... مباحثی که توی این دوره مرور میکنیم میتونه ما رو از سطح صفر برنامه نویسی ios به صد …
فصلِ: 6 , تعداد قسمت ها: 194 , سطح: صفر تا صد

این فصل در یک نگاه:

در این فصل آپدیت های مربوط به دوره را قرار میدهیم…

با سلام و خسته نباشد خدمت کلیک سایتی های عزیز در ادامه با توضیحات مختصری درمورد دوره ی react native با ما همراه باشید: React Native چیست؟ قطعا یکی از آرزوهای برنامه نویسان این میباشد که ب…
فصلِ: 5 , تعداد قسمت ها: 51 , سطح: صفر تا صد

این فصل در یک نگاه:

اموزش پرداخت درون برنامه ای بازار در یونیتی-اموزش خرید سکه در بازی-اموزش خرید مصرفی و غیر مصرفی…

توضیحات کلی مجموعه: آموزش مقدماتی تا پیشرفته یونیتی(ساخت بازی توپ چرخنده-اموزش بازی دوبعدی-اموزش بازی سه بعدی اول شخص شوتر-اموزش بازی سه بعدی رالی (ماشین سواری))   سرفصلهای دوره: نصب ی…
فصلِ: 2 , تعداد قسمت ها: 68 , سطح: صفر تا صد

این فصل در یک نگاه:

آموزش ساخت یک فروشگاه کوچک-آموزش کار با bottom navigation -آموزش کار با تب بار-آموزش کار با scrollview - آموزش کار با ان…

توضیحات کلی مجموعه: آموزش دوره فلاتر از پایه تا پیشرفته(این دوره به دوره متخصص فلاتر تغییر پیدا کرد) درسال های گذشته تعدا زیادی فریمورک معرفی شد که هرکدام تجربه خاص خودش رو داشت،اما فلاتر …

تولید شده توسط کلیک سایت

پشتیبانی آنلاین
آماده پاسخگویی هستیم
انتخاب تصویر جهت ارسال:
در حال ضبط صدا

(جهت توقف و یا لغو ضبط از دکمه های زیر استفاده کنید)

توقف و ارسال :
لغو ضبط
در حال حاضر تمامی کارشناسان آفلاین هستند. همواره می توانید با شماره تلگرام / واتساپ 09010005000 به صورت آنلاین با ما در ارتباط باشید. جهت ورود به واتساپ کلیک کنید
0 پیام جدید
پشتیبان در حال تایپ ...
ارسال تصویر ضبط صدا
0 کارشناسان آنلاین می باشند
این گفت و گو توسط پشتیبان به اتمام رسید