class SetUpAndTearDownExampleTestCase: XCTestCase {
override class func setUp() { // 1.
// This is the setUp() class method.
// It is called before the first test method begins.
// Set up any overall initial state here.
}
override func setUpWithError() throws { // 2.
// This is the setUpWithError() instance method.
// It is called before each test method begins.
// Set up any per-test state here.
}
override func setUp() { // 3.
// This is the setUp() instance method.
// It is called before each test method begins.
// Use setUpWithError() to set up any per-test state,
// unless you have legacy tests using setUp().
}
func testMethod1() throws { // 4.
// This is the first test method.
// Your testing code goes here.
addTeardownBlock { // 5.
// Called when testMethod1() ends.
}
}
func testMethod2() throws { // 6.
// This is the second test method.
// Your testing code goes here.
addTeardownBlock { // 7.
// Called when testMethod2() ends.
}
addTeardownBlock { // 8.
// Called when testMethod2() ends.
}
}
override func tearDown() { // 9.
// This is the tearDown() instance method.
// It is called after each test method completes.
// Use tearDownWithError() for any per-test cleanup,
// unless you have legacy tests using tearDown().
}
override func tearDownWithError() throws { // 10.
// This is the tearDownWithError() instance method.
// It is called after each test method completes.
// Perform any per-test cleanup here.
}
override class func tearDown() { // 11.
// This is the tearDown() class method.
// It is called after all test methods complete.
// Perform any overall cleanup here.
}
}
func testNasaData() throws {
let rawResponse = """
{
"description": "The past year was extraordinary for the discovery of extraterrestrial fountains and flows -- some offering new potential in the search for liquid water and the origin of life beyond planet Earth.. Increased evidence was uncovered that fountains spurt not only from Saturn's moon Enceladus, but from the dunes of Mars as well. Lakes were found on Saturn's moon Titan, and the residual of a flowing liquid was discovered on the walls of Martian craters. The diverse Solar System fluidity may involve forms of slushy water-ice, methane, or sublimating carbon dioxide. Pictured above, the light-colored path below the image center is hypothesized to have been created sometime in just the past few years by liquid water flowing across the surface of Mars.",
"copyright": "MGS, MSSS, JPL, NASA",
"title": "A Year of Extraterrestrial Fountains and Flows",
"url": "https://apod.nasa.gov/apod/image/0612/flow_mgs.jpg",
"apod_site": "https://apod.nasa.gov/apod/ap061231.html",
"date": "2006-12-31",
"media_type": "image",
"hdurl": "https://apod.nasa.gov/apod/image/0612/flow_mgs_big.jpg"
}
"""
// XCTUnwrap嘗試解開可選的內容,如果可選的內容為nil,則會拋出錯誤(並因此導致測試失敗)
let data = try XCTUnwrap(rawResponse.data(using: .utf8))
let nasaData = try XCTUnwrap(JSONDecoder().decode(NasaData.self, from: data))
XCTAssertEqual(nasaData.date, "2006-12-31")
XCTAssertEqual(nasaData.mediaType, "image")
}
2. 對 API 做異步測試:使用 XCTestExpectation(這算是整合測試,不是單元測試)
func testDataManagerGetNasaData() {
// 宣告expectation
let expect = expectation(description: "Get nasa data")
let dataManager = DataManager()
let urlString = "https://raw.githubusercontent.com/cmmobile/NasaDataSet/main/apod.json"
dataManager.getNasaData(urlString: urlString) { result in
switch result {
case .success(_):
XCTAssert(true)
case .failure(_):
XCTAssert(false)
}
// 達成期望,讓test runner知道可以繼續
expect.fulfill()
}
// 等待期望被實現,或者10秒後超時
wait(for: [expect], timeout: 10.0)
}
3. 對 ViewModel 或是 Manager 測試:當遇到 API 或資料庫,可用 Protocol 抽離實作並依賴注入(DI)
建立呼叫 DataProvider 的 Protocol,並用依賴注入(DI)
class DataProvider: DataProviderDelegate{
func getData(url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void){
let task = URLSession.shared.dataTask(with: url) {
(data, response, error) in completionHandler(data, response, error)
}
task.resume()
}
}
protocol DataProviderDelegate: class {
func getData(url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void)
}
class DataManagerWithDI{
enum DataManagerError: Error {
case urlError
case noData
}
weak var dataProvider: DataProviderDelegate?
init(dataProvider: DataProviderDelegate) {
self.dataProvider = dataProvider
}
func getNasaData(completionHandler: @escaping (Result<[NasaData], Error>) -> Void) {
guard let url = URL(string: "https://raw.githubusercontent.com/cmmobile/NasaDataSet/main/apod.json") else {
completionHandler(.failure(DataManagerError.urlError))
return
}
dataProvider?.getData(url: url) {
(data, response, error) in
if let error = error {
completionHandler(.failure(error))
return
}
if let data = data,
let nasaDataArray = try? JSONDecoder().decode([NasaData].self, from: data) {
completionHandler(.success(nasaDataArray))
} else {
completionHandler(.failure(DataManagerError.noData))
}
}
}
}
測試的時候,建立 FakeDataProvider ,注入並且測試
func testDataManagerWithDIGetNasaData() throws {
let fakeDataProvider = FakeDataProvider()
let dataManagerWithDI = DataManagerWithDI(dataProvider: fakeDataProvider)
dataManagerWithDI.dataProvider = fakeDataProvider
dataManagerWithDI.getNasaData {
result in
switch result{
case .success(_):
XCTAssert(true)
case .failure(_):
XCTAssert(false)
}
}
}
class FakeDataProvider: DataProviderDelegate{
func getData(url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void){
let dataString = """
[{
"description": "The past year was extraordinary for the discovery of extraterrestrial fountains and flows -- some offering new potential in the search for liquid water and the origin of life beyond planet Earth.. Increased evidence was uncovered that fountains spurt not only from Saturn's moon Enceladus, but from the dunes of Mars as well. Lakes were found on Saturn's moon Titan, and the residual of a flowing liquid was discovered on the walls of Martian craters. The diverse Solar System fluidity may involve forms of slushy water-ice, methane, or sublimating carbon dioxide. Pictured above, the light-colored path below the image center is hypothesized to have been created sometime in just the past few years by liquid water flowing across the surface of Mars.",
"copyright": "MGS, MSSS, JPL, NASA",
"title": "A Year of Extraterrestrial Fountains and Flows",
"url": "https://apod.nasa.gov/apod/image/0612/flow_mgs.jpg",
"apod_site": "https://apod.nasa.gov/apod/ap061231.html",
"date": "2006-12-31",
"media_type": "image",
"hdurl": "https://apod.nasa.gov/apod/image/0612/flow_mgs_big.jpg"
}]
"""
let data = Data(dataString.utf8)
completionHandler(data, nil ,nil)
}
}
4. 性能測試:使用Measure Block
class PerformanceTests: XCTestCase {
lazy var testData: [Int] = {
return (0..<100000).map { Int($0) }
}()
func test_performance_getEvenNumbers_forEachLoop() {
//這個區塊會運行10次,收集平均執行的時間和運行的標準偏差
measure {
var evenNumbers: [Int] = []
testData.filter { number in number % 2 == 0}.forEach { number in evenNumbers.append(number) }
}
}
func test_performance_getEvenNumbers_forLoop() {
//這個區塊會運行10次,收集平均執行的時間和運行的標準偏差
measure {
var evenNumbers: [Int] = []
for number in testData {
if number % 2 == 0 {
evenNumbers.append(number)
}
}
}
}
}
性能測試需要設置Baseline來驗證是否通過測試,沒有設置的會提示No baseline average for Time。