class ViewController: UIViewController { @IBOutlet weak var xTextField: UITextField! @IBOutlet weak var yTextField: UITextField! @IBOutlet weak var resultLabel: UILabel! @IBOutlet weak var urlTextField: UITextField! @IBOutlet weak var responseTextView: UITextView! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) self.clearData() } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } func clearData(){ self.resultLabel.text = "" self.responseTextView.text = "" } @IBAction func calculateButton_Touch(_ sender: Any) { let x = Math.stringToInt(string: self.xTextField.text) let y = Math.stringToInt(string: self.yTextField.text) var outputString = "invalid input" if let x = x, let y = y { let result = Math.add(x: x, y: y) outputString = "\(result)" } self.resultLabel.text = outputString } @IBAction func requestButton_Touch(_ sender: Any) { if let url = urlTextField.text { Network.dataTask(urlString: url) { (data, response, error) in guard error == nil else { self.responseTextView.text = "\(error!.localizedDescription)" return } if let data = data { if let jsonString = String(data: data, encoding: .utf8){ DispatchQueue.main.async { self.responseTextView.text = jsonString class ViewController: UIViewController {
@IBOutlet weak var xTextField: UITextField!
@IBOutlet weak var yTextField: UITextField!
@IBOutlet weak var resultLabel: UILabel!
@IBOutlet weak var urlTextField: UITextField!
@IBOutlet weak var responseTextView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.clearData()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func clearData(){
self.resultLabel.text = ""
self.responseTextView.text = ""
}
@IBAction func calculateButton_Touch(_ sender: Any) {
let x = Math.stringToInt(string: self.xTextField.text)
let y = Math.stringToInt(string: self.yTextField.text)
var outputString = "invalid input"
if let x = x, let y = y {
let result = Math.add(x: x, y: y)
outputString = "\(result)"
}
self.resultLabel.text = outputString
}
@IBAction func requestButton_Touch(_ sender: Any) {
if let url = urlTextField.text {
Network.dataTask(urlString: url) { (data, response, error) in
guard error == nil else {
self.responseTextView.text = "\(error!.localizedDescription)"
return
}
if let data = data {
if let jsonString = String(data: data, encoding: .utf8){
DispatchQueue.main.async {
self.responseTextView.text = jsonString
}
}
}
}
}
}
}
(2.) Math
add: 將2個數字相加並回傳
stringToInt: string轉int並回傳結果, 若失敗回傳nil
class Math: NSObject {
class func add(x: Int, y: Int) -> Int {
let result = x + y
return result
}
class func stringToInt(string: String?) -> Int? {
var result:Int? = nil
if let string = string {
result = Int(string)
}
return result
}
}
(3.) Network dataTask:
發送一個request並回傳response
class Network: NSObject {
class func dataTask(urlString: String, responseHandler: @escaping (Data?, URLResponse?, Error?) -> Swift.Void) {
let url = URL(string: urlString)
if let url = url {
let request = URLRequest(url: url)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
print("Retrieved data")
responseHandler(data, response, error)
}
task.resume()
} else {
let myError = NSError(domain:"", code: -9999, userInfo:nil)
responseHandler(nil, nil, myError)
}
}
}
建置Unit Test
首先, 如果您的project尚未加入Unit Test, 可以從File -> New -> Target 新建一個Unit Test Bundle.
import XCTest
class TestProjectTests: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}
我們可以注意到, Unit Test用的class必須import XCTest並extend XCTestCase,
import XCTest
class MathTest: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testAdd() {
let a = Math.add(x: 3, y: 5)
let b = Math.add(x: 64, y: -70)
let c = Math.add(x: 999, y: 0)
XCTAssert(a == 8, "a should be 8")
XCTAssert(b == -6, "b should be -6")
XCTAssert(c == 999, "c should be 999")
}
func testStringToInt() {
let a = Math.stringToInt(string: "9")
let b = Math.stringToInt(string: "-55")
let c = Math.stringToInt(string: "sdfsks")
XCTAssert(a == 9, "a should be 8")
XCTAssert(b == -55, "b should be -55")
XCTAssert(c == nil, "c should be nil")
}
}
import XCTest
class TestNetwork: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testDataTask() {
let urlString = "http://opendata2.epa.gov.tw/UV/UV.json"
Network.dataTask(urlString: urlString) { (data, response, error) in
let requestUrl = response?.url?.absoluteString
XCTAssert(requestUrl == urlString, "wrong URL!")
}
}
}
(1.) test class的Viewcontroller需要用storyboard.instantiateViewController的方式去宣告, 否則IBOutlet元件會為nil
(2. ) 必須呼叫 vc.loadView() 才能啟動Viewcontroller的生命週期
import XCTest
class TestViewController: XCTestCase {
override func setUp() {
super.setUp()
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testClearData() {
let storyboard = UIStoryboard(name: "Main", bundle: Bundle(for: ViewController.self))
let vc = storyboard.instantiateViewController(withIdentifier: "ViewController") as! ViewController
vc.loadView() // call life cycle
vc.clearData()
XCTAssert(vc.resultLabel!.text == "", "vc.resultLabel!.text should be empty ")
XCTAssert(vc.responseTextView!.text == "", "vc.responseTextView!.text should be empty ")
}
}