Easy Unit Tests in Swift
ได้มีโอกาสมาเขียน iOS Application ด้วยภาษา Swift และ Xcode ซึ่งเป็นภาษาที่สามารถเขียนในรูปแบบ OOP และเขียนได้สั้นกระชับมากและยังมีการป้องกันการผิดพลาดของโค้ด เช่น การใช้ตัวแปรที่ไม่ได้ถูกกำหนดค่า การใช้ค่า null อย่างไม่เหมาะสมถือเป็นการเรียนรู้อีกภาษาที่น่าตื่นเต้นมากในปีนี้ และในบทความนี้จะมาแนะนำวิธีการการเขียน Unit Test แบบให้เข้าใจง่าย ๆ เพื่อเป็นแนวทางสำหรับใครที่อยากจะเริ่มต้นเขียน Unit Test เรามาเริ่มกันเลยครับ…
ก่อนอื่นเรามาดูกันว่าการเขียน Unit Test มีประโยชน์อย่างไรบ้างในกระบวนการที่สำคัญในการพัฒนาซอฟต์แวร์
- การยืนยันความถูกต้องของโค้ด (Code Validation): Unit Test ช่วยยืนยันว่าโค้ดทำงานได้ตามที่คาดหวัง และไม่มีข้อผิดพลาด (bugs) การทดสอบช่วยลดความเสี่ยงที่โค้ดอาจมีข้อผิดพลาดหรือบั๊กที่ไม่รู้ตัว
- การรักษาความสมบูรณ์ของโค้ด (Code Maintainability): เมื่อโค้ดมีการเปลี่ยนแปลง, Unit Test ช่วยในการตรวจสอบว่าการเปลี่ยนแปลงไม่ทำให้เกิดบั๊กใหม่หรือทำให้เสียหาย
- การเพิ่มความมั่นใจ (Confidence Building):
Unit Test ช่วยให้ทีมพัฒนามีความมั่นใจว่าการเปลี่ยนแปลงที่ทำไปไม่ได้ทำให้เกิดปัญหาที่ไม่คาดคิด - การทำให้ระบบงานได้ตามที่กำหนด (Specification Verification):
Unit Test ช่วยตรวจสอบว่าโค้ดทำตามข้อกำหนด (specifications) และความต้องการ (requirements) หรือไม่ - การลดเวลาในการตรวจสอบและแก้บั๊ก (Debugging Time Reduction):
การทำ Unit Test ช่วยลดเวลาในการตรวจสอบและแก้บั๊ก, เนื่องจากการทดสอบช่วยในการระบุตำแหน่งของบั๊กได้ทันที - ส่งเสริมการพัฒนาแบบ Agile และ Continuous Integration:
Unit Test เป็นส่วนหนึ่งของกระบวนการพัฒนาแบบ Agile และ Continuous Integration, ช่วยในการทำงานร่วมกับการพัฒนาและทดสอบในระบบทั้งหมด - การพัฒนาซอฟต์แวร์ที่ยืดหยุ่น (Flexible Software Development):
Unit Test ช่วยสนับสนุนการพัฒนาซอฟต์แวร์ที่ยืดหยุ่น, ทำให้การเปลี่ยนแปลงหรือเพิ่มฟีเจอร์ใหม่ไม่ส่งผลกระทบต่อการทำงานของระบบ
การเขียน Unit Test นอกจากนี้ยังช่วยในการเขียนโค้ดที่มีคุณภาพสูง, การทดสอบที่ถูกต้อง, และทำให้การพัฒนาเป็นไปได้อย่างมีประสิทธิภาพมากยิ่งขึ้น
ทำความเข้าใจเกี่ยวกับการเขียน Unit Test แบบ AAA (arrange- act-assert)
หลักการเขียน Unit Test แบบ AAA คือ Arrange, Act, Assert ซึ่งเป็นกระบวนการที่ช่วยในการออกแบบและเขียน Unit Test ให้มีความกระชับและเข้าใจง่าย ซึ่งแบ่งได้เป็นขั้นตอนดังต่อไปนี้:
Arrange (จัดเตรียม):
- กำหนดเงื่อนไขเริ่มต้นและสร้างวัตถุที่เกี่ยวข้องในการทดสอบ
- กำหนดค่าที่จะใช้ในการทดสอบ เช่น ข้อมูลทดสอบ, อ็อบเจกต์ทดสอบ, หรือสถานะเริ่มต้น
Act (ดำเนินการ):
- ทำการเรียกใช้วัตถุหรือฟังก์ชันที่ต้องการทดสอบ
- ดำเนินการที่เกี่ยวข้องกับการทดสอบ
Assert (ตรวจสอบ):
- ตรวจสอบผลลัพธ์หรือสถานะหลังจากที่มีการดำเนินการ
- ทำการตรวจสอบว่าผลลัพธ์ตรงตามที่คาดหวังหรือไม่
- ถ้าผลลัพธ์ไม่ตรงตามที่คาดหวังให้ทำการเก็บข้อมูลที่สามารถให้ข้อมูลในการวิเคราะห์ได้
พื้นฐานการเขียนโค้ดในรูปแบบ MVVM pattern
MVVM ย่อมาจาก Model-View-ViewModel เป็นรูปแบบสถาปัตยกรรมซอฟต์แวร์ที่แบ่งโค้ดออกเป็น 3 ส่วนหลักๆ คือ
- Model: เก็บข้อมูลและทำหน้าที่ประมวลผลข้อมูล
- View: แสดงข้อมูลบนหน้าจอ
- ViewModel: ทำหน้าที่เชื่อมระหว่าง Model และ View
เรามาเริ่มการเขียนโค้ดด้วย Xcode ร่วมกับ SwiftUI ในรูปแบบ MVVM กันเลยครับ
- สร้าง Model: สร้าง Model ที่จะทำหน้าที่คำนวณการบวกและลบของเครื่องคิดเลข
import Foundation
struct CalculatorModel {
func add(_ number1: Int, _ number2: Int) -> Int {
return number1 + number2
}
func subtract(_ number1: Int, _ number2: Int) -> Int {
return number1 - number2
}
}
2. สร้าง ViewModel: สร้าง ViewModel ที่เรียกใช้ Model เพื่อทำหน้าที่จัดการกับการคำนวณและการอัปเดต View
import Foundation
import Combine
class CalculatorViewModel: ObservableObject {
private var model = CalculatorModel()
@Published var result: Int = 0
func performAddition(number1: Int, number2: Int) {
result = model.add(number1, number2)
}
func performSubtraction(number1: Int, number2: Int) {
result = model.subtract(number1, number2)
}
}
3. สร้าง View: สร้าง SwiftUI View ที่จะแสดง UI ของเครื่องคิดเลข
import SwiftUI
struct CalculatorView: View {
@ObservedObject var viewModel: CalculatorViewModel
@State private var number1 = ""
@State private var number2 = ""
var body: some View {
VStack {
TextField("Enter number 1", text: $number1)
.keyboardType(.numberPad)
.padding()
TextField("Enter number 2", text: $number2)
.keyboardType(.numberPad)
.padding()
HStack {
Button("Add") {
if let num1 = Int(number1), let num2 = Int(number2) {
viewModel.performAddition(number1: num1, number2: num2)
}
}
Button("Subtract") {
if let num1 = Int(number1), let num2 = Int(number2) {
viewModel.performSubtraction(number1: num1, number2: num2)
}
}
}
.padding()
Text("Result: \(viewModel.result)")
.padding()
}
}
}
#Preview {
CalculatorView(viewModel: CalculatorViewModel())
}
5. ทำการ Run ด้วยคำสั่ง Cmd + R จะแสดงผลลัพธ์ดังรูป
6. เขียน Unit Test สำหรับ ViewModel: เขียน Unit Test ด้วย XCTest เพื่อทดสอบการคำนวณและการอัปเดตผลลัพธ์ของ ViewModel
import XCTest
@testable import MyCalculator
class CalculatorViewModelTests: XCTestCase {
var viewModel: CalculatorViewModel!
// Arrange
override func setUpWithError() throws {
viewModel = CalculatorViewModel()
}
// Teardown
override func tearDownWithError() throws {
viewModel = nil
}
// Act and Assert
func testPerformAddition() throws {
// Arrange
let number1 = 5
let number2 = 8
// Act
viewModel.performAddition(number1: number1, number2: number2)
// Assert
XCTAssertEqual(viewModel.result, 8)
}
// Act and Assert
func testPerformSubtraction() throws {
// Arrange
let number1 = 8
let number2 = 3
// Act
viewModel.performSubtraction(number1: number1, number2: number2)
// Assert
XCTAssertEqual(viewModel.result, 5)
}
}
- ทำการ Run ด้วยคำสั่ง Cmd + U จะแสดงผลลัพธ์ดังรูป
ไม่ต้องตกใจถ้าผลลัพธ์ที่ได้จะแสดง Error ออกมาตามรูปด้านบน เรามาถูกทางแล้วเพราะว่าตัวเลข number2 ในบรรทัดที่ 28 เมื่อเอามาบวกรวมกันแล้วเรา Assert ไว้ว่าจะต้องมีค่าเท่ากับ 8 ตามบรรทัดที่ 34 แสดงว่าถ้าไม่เท่ากับ 8 แล้วต้องมีอะไรบางอย่างผิดพลาด นั้นก็คือต้องเปลี่ยนตัวเลข number2 จาก 8 เป็น 3 แล้วลอง Test ใหม่อีกครั้ง
ผลลัพธ์จากรูปด้านบนจะเห็นว่า Test ที่ได้มีผล Pass ทั้งสองฟังก์ชันที่มีการเขียน Test เท่านี้เราก็ได้ทำการเขียน Unit Test เพื่อทดสอบว่าฟังก์ชัน performAddition() และ performSubtraction() ทำงานได้ถูกต้อง อย่างไรก็ตามการโค้ดใน Application จริงในแต่ละฟังก์ชันมีความซับซ้อนและมี Business logic ที่ซับซ้อนการ Refactor code เพื่อให้แต่ละฟังก์ชันให้มี Line of code น้อยลง ให้แต่ละฟังก์ชันทำหน้าที่อย่างใดอย่างหนึ่งก็ช่วยให้เราเขียน Unit Test ได้ง่ายขึ้นและครอบคลุมมากยิ่งขึ้น
สามารถดาวโหลด Source code ได้ที่นี้