WWDC μμμ μ¬κΈ°λ₯Ό ν΄λ¦ν΄μ£ΌμΈμ ('-' Ρ )Π
ν΄λΉ μΈμ μμλ ν¬κ² Introduction To XCTest, Test plans, Continuous integration workflows μ΄λ κ² 3κ°μ§ ν ν½μ λν΄ μκΈ°νκ³ μμ΅λλ€. λΈλ‘κ·Έμμλ λ§μ§λ§ ν ν½μ μ μΈν μμ λ κ°μ§ λ΄μ©μ λν΄μλ§ λ€λ£¨κ³ μ νλ μ°Έκ³ λΆνλ립λλ€! (CIλ ... λμ€μ.. 컀λ°μ¨ ε( ε Λ³Λ)Π· βº )
Introduction to XCTest
π§ͺ XCTestλ ?
Xcodeμμ μ 곡λλ μλν ν μ€νΈ νλ μμν¬.
ν μ€νΈλ₯Ό μ€μ νκ³ μ€ννλλ° λμμ΄ λλ€
β κ·Όλ° ν μ€νΈλ μ ν΄μΌλ κΉ?
ν μ€νΈλ μ½λμμ λ²κ·Έλ₯Ό μ°Ύλλ° λμμ΄ λλ λͺ¨λ νλ‘μ νΈ κ°λ°μ μ€μν λ¨κ³μ΄κΈ° λλ¬Έ.
λν ν μ€νΈλ₯Ό μ¬μ©νμ¬ μꡬ μ¬νμ μ½λ©ν μλ μμ (TDDλ₯Ό μκΈ°νλκ±ΈκΉλ .. π€)
β οΈ Test Pyramid
1. Unit Test (λ¨μ ν μ€νΈ)
μ§§κ³ κ°λ¨νλ©° λ§€μ° λΉ λ₯΄κ² μ€νλ¨.
λͺ¨λ ν μ€νΈμ κΈ°μ΄κ° λλ―λ‘ λͺ¨λ ν¨μλ₯Ό λ€λ£° μ μλλ‘ λ§μ λ¨μ ν μ€νΈλ₯Ό μμ±ν΄μΌ ν¨.
νΌλΌλ―Έλμ κΈ°μ΄μ΄λ©° μΌλ°μ μΌλ‘ νλμ μ½λ, μ¦ ν¨μλ₯Ό κ²μ¦νλ λ° λμμ΄ λ¨ (ν¨μμ λ³μλ₯Ό μ λ ₯νκ³ μμλλ μΆλ ₯μ λ°ννλμ§ νμΈνλ λ°©μ)
2. Integration Test (ν΅ν© ν μ€νΈ)
μλ‘ λ€λ₯Έ μ»΄ν¬λνΈκ° ν¨κ» μ¬λ°λ₯΄κ² μλνλμ§ νμΈνκΈ° μν΄ κ°λ³ μλΈμμ€ν λλ ν΄λμ€ ν΄λ¬μ€ν°λ₯Ό λμμΌλ‘ ν΄μΌ ν¨.
μΌλ°μ μΌλ‘ λ¨μ ν μ€νΈλ§νΌ λ§μ ν΅ν© ν μ€νΈκ° νμνμ§ μμ.
μ€ννλ λ° μκ°μ΄ μ‘°κΈ λ 걸릴 μ μμ§λ§ ν λ²μ λ λ§μ μ±μ ν μ€νΈν μ μμ.
ν΅ν© ν μ€νΈλ λ¨μ ν μ€νΈ μμ μμΉνλλ°, μ΄λ λ ν° μ½λ λΆλΆμ ν μ€νΈνκΈ° μ μ κ°λ³ ν¨μκ° μ¬λ°λ₯΄κ² μλνλμ§ νμΈν΄μΌ νκΈ° λλ¬Έ.
μ½λμ λ ν° λΆλΆμ κ²μ¦νλ λ° μ¬μ©λ¨.
3. User Interface Test
μ±μ μ¬μ©μ λ¨κ³λ³ λμμ κ΄μ°°ν¨
μ΄λ₯Ό ν΅ν΄ μ±μ΄ μ€μ λ‘ μ¬μ©μκ° κΈ°λνλ λλ‘ μλνλμ§ νμΈ κ°λ₯.
UI ν μ€νΈλ μ€ννλ λ° κ°μ₯ μ€λ μκ°μ΄ 걸리μ§λ§ λͺ¨λ κ²μ΄ μ¬λ°λ₯΄κ² μλνλμ§ νμΈνλ λ° νμμ .
λν μ±μ UIκ° λ μμ£Ό λ³κ²½λ μ μκΈ° λλ¬Έμ UI ν μ€νΈλ λ λ§μ μ μ§ κ΄λ¦¬κ° νμ.
ποΈ Types of XCTest
XCTestλ μ΄ 3κ°μ ν μ€νΈ νμ μ μ§μν¨
1) Unit Test(Unit & Integration) 2) UI Test(Blackbox Test) 3) Performance Test
μ€λμ μ£Όμ λ Unit Test & UI Test
π€ How to start test in Xcode
Xcode νλ‘μ νΈμμ ν μ€νΈλ₯Ό μμνλ κ°μ₯ μ¬μ΄ λ°©λ²μ μ νλ‘μ νΈλ₯Ό μμν λ μ λ ν μ€νΈμ UI ν μ€νΈλ₯Ό λͺ¨λ ν¬ν¨νλλ‘ μ ννλ κ² (κΈ°μ‘΄ νλ‘μ νΈμ μΆκ°νλ κ²λ κ°λ₯)
Unit Test, UI Test νκ²κ³Ό κΈ°λ³Έ ν νλ¦Ώ μ½λκ° μλμΌλ‘ μμ±λ¨
// How to use XCTest
import XCTest
@testable import MyCounterApp
// μ¬κΈ°μ XCTestCaseλ₯Ό μμλ°κ³ μλ μ΄μ λ μ΄λ₯Ό μμλ°μμΌλ‘μ¨ Xcodeκ° ν΄λΉ ν΄λμ€ λ΄λΆμ λ©μλλ€μ
// ν
μ€νΈν λ μ¬μ©ν μ μκ² νκΈ° μν΄μμ΄λ€ !
class MyCounterTests: XCTestCase {
func testIncrementCounter() {
var counter = Counter()
counter.increment()
XCTAssertEqual(counter.value, 1, "Counter was not incremented: \(counter)")
}
}
- ν μ€νΈ λ©μλ λ€μ΄λ° κ·μΉ: ν μ€νΈ μΌμ΄μ€λ‘ μ¬μ©νλ €λ κ° λ©μλλ testλΌλ λ¨μ΄λ‘ μμν΄μΌ νλ©°, κ·Έ λ€μμλ 무μμ ν κ²μΈμ§λ₯Ό λνλ΄λ μ΄λ¦μ μ§μ ν΄μΌν¨.
- Assertion API: ν μ€νΈ λ΄λΆμμ assertion APIλ μμ€ μ½λλ₯Ό νκ°νκ³ μ ν¨μ±μ κ²μ¬νλ λ° μ¬μ©λ¨. μ΄ κ²½μ° XCTAssertEqualμ μ£Όμ΄μ§ μ²μ λ κ°μ λΉκ΅νμ¬ λμΌνμ§ νμΈν¨.
- SetUp, TearDown: SetUp λ©μλλ κ° ν μ€νΈ μΌμ΄μ€κ° μ€νλκΈ° μ μ νΈμΆλ¨. κ·Έλ° λ€μ XCTestκ° ν μ€νΈ λ©μλλ₯Ό μ€ννκ³ κ·Έ νμ tearDownμ μ¬μ©νμ¬ λ°μ΄ν° λλ μ±μ μ μ μνμ λν λ³κ²½ μ¬νμ μ 리νμ¬ ν μ€νΈκ° νμ ν μ€νΈμ μν₯μ μ€ μ μλ λ΄μ©μ λ¨κΈ°μ§ μλλ‘ ν΄μΌν¨.
Demo
Unit Test
// μ λ©μλλ₯Ό ν
μ€νΈν΄λ³Όκ²μ!(DistanceCalculator classμ instance λ©μλ)
func city(forName city: String) -> City? {
return cities[city]
}
κ°μ₯ λ¨Όμ DistanceCalculator ν΄λμ€λ₯Ό ν μ€νΈνκΈ° μν DistanceCalculatorTests ν μ€νΈ ν΄λμ€λ₯Ό μμ±.
class DistanceCalculatorTests: XCTestCase {
func testCoordinatesOfSeattle() throws {
// λ¨Όμ ν
μ€νΈν κ°μ²΄λ₯Ό μμ±ν΄μ€λ€.
let calculator = DistanceCalculator()
// city λ©μλλ μ΅μ
λ νμ
μ 리ν΄νκΈ° λλ¬Έμ μ΄λ μ΅μ
λμ΄ μλκΈ° μνκΈ° λλ¬Έμ XCTUnwrapμ΄λΌλ
// APIλ₯Ό νμ©νλ€!
// κ·Έλ¦¬κ³ μ΄λ μ΄ λ©μλλ μλ¬λ₯Ό throwν μ μλ λ©μλμ΄κΈ° λλ¬Έμ testCoordinatesOfSeattel
// λ©μλ μ체λ μλ¬λ₯Ό λμ§ μ μλ throws ν€μλλ₯Ό λΆμ¬μ€μΌνλ€.
let city = try XCTUnwrap(calculator.city(forName: "Seattle"))
XCTAssertEqual(city.coordinates.latitude, 47.61)
XCTAssertEqual(city.coordinates.longitude, -122.33)
}
}
var calculator: DistanceCalculator!
override func setUp() {
super.setUp()
calculator = DistanceCalculator()
}
override func tearDown() {
calculator = nil
super.tearDown()
}
func testSanFransicoToNewYork() throws {
let distanceInMiles = try calculator.distanceInMiles(from: "San Fransico", to: "New York City")
// μ¬κΈ°μ κ³μ°λ κ°μ 2571.6947594 μ΄λ° μμμ μΌλ‘ λμ€λλ° λλ μ΄λμ λ κ·Όμ¬μΉμλ§ λλ¬νλ©΄
// ν
μ€νΈλ₯Ό ν΅κ³Όμν€κ³ μΆκΈ° λλ¬Έμ μ΄λ° κ²½μ°μλ accuracy νλΌλ―Έν°λ₯Ό μΆκ°ν΄μ κ·Όμ¬μΉ μ λλ₯Ό μ‘°μ ν μ μλ€!
XCTAssertEqual(distanceInMiles, 2571, accuracy: 1)
}
λλ²μ§Έ ν μ€νΈ λ©μλμμλ DistanceCalculator classλ₯Ό ν μ€νΈν΄μΌνκΈ° λλ¬Έμ κ°μ²΄λ₯Ό μμ±ν΄μΌν¨. μ΄λ 첫λ²μ§Έ ν μ€νΈ λ©μλμ κ°μ²΄λ₯Ό μμ±νλ λΆλΆμ΄ μ€λ³΅λλ κ²μ λ³Ό μ μμ
β‘οΈ ν΄λμ€μ νλ‘νΌν°λ‘ μ μΈν΄μ μ€λ³΅λ μ½λλ₯Ό μ€μΌ μ μμ. κ·Έλ¦¬κ³ κ° ν μ€νΈ μΌμ΄μ€ μ μ μ€νλλ setUp λ©μλμμ μ΄κΈ°νμμΌμ£Όλ©΄ λ¨.
μμ λ ν μ€νΈ λ©μλλ ν¨μμ μ ν¨ν μ λ ₯μ λ°κ³ μ ν¨ν μ λ ₯μ΄ λ°νλλμ§ νμΈνλ ν μ€νΈ.
μ΄μ λ¬λ¦¬ μ€λ₯λ μ¬λ°λ₯΄κ² μ²λ¦¬λλμ§ νμΈνκ³ μΆμ κ²½μ°λ ν μ€νΈ μμ±μ΄ κ°λ₯ν¨
func testCupertinoNotRecognized() {
XCTAssertThrowsError(try calculator.distanceInMiles(from: "Cupertino", to: "New York City")) { error in
XCTAssertEqual(error as? DistanceCalculator.Error, .unknownCity("Cupertino"))
}
}
XCTAssertThrowsErrorλ₯Ό ν΅ν΄ λ¨Όμ μλ¬κ° λ°μνκ³ μλμ§ νμΈνκ³ , XCTAssertEqualλ‘ κΈ°λν μ€λ₯μΈμ§ νμΈνλ κ²λ κ°λ₯ν¨.
UI Test
override func setUp() {
// μ£Όλ‘ testκ° failνλ€λ κ²μ μμμΉλͺ»ν UI μνλ₯Ό λ§λ₯λ¨λ Έλ€λ κ²μ΄λ€.
// λμ΄μ μνΈμμ©μ΄ λΆκ°λ₯ν μ μκΈ° λλ¬Έμ continueνμ§ μλλ‘ μ€μ ν΄μ€μΌνλ€.
continueAfterFailure = false
app.launch()
}
func testMilesToParis() {
// Discover tap ν
app.tabBars.buttons["Discover"].tap()
// μ΄κΈ° μνμΈ San Fransico labelμ΄ λ³΄μ¬μ§κ³ μλμ§
// isHittable: ν΄λΉ μμκ° μ‘΄μ¬νλμ§ κ·Έλ¦¬κ³ νλ©΄μ νμλλμ§λ₯Ό νμΈ
XCTAssert(app.staticText["San Fransico"].isHittable)
// μλν μν©μΌλ‘ μ±μ μνλ₯Ό λ³νμν€κΈ° μν΄ swipeLeft μ€ν
let sfImage = app.images["san-fransico"]
sfImage.swipeLeft()
// κ·Έ νμ μνλ μ 보λ€μ΄ μ 보μ¬μ§κ³ μλμ§ νμΈ
XCTAssert(app.staticTexts["Paris"].isHittable)
XCTAssert(app.statictexts["5586 miles away"].isHittable)
}
ν μ€νΈ μ½λλ₯Ό μμ±νλ κ³Όμ μμ μνλ UI μ»΄ν¬λνΈμ μ κ·Όνκ³ μΆμΌλ μ νν μ΄λ¦ νΉμ μλ³μλ₯Ό λͺ¨λ₯΄λ κ²½μ°, λΈλ μ΄ν¬ ν¬μΈνΈλ₯Ό 건 ν po λͺ λ Ήμ΄λ₯Ό ν΅ν΄μ νμ¬ μ±μ νμλκ³ μλ λͺ¨λ μ»΄ν¬λνΈμ λν μ 보, νΉμ μνλ νΉμ νμ μ λν μ 보λ₯Ό μ»μ μ μμ.
po app
po app.images
κ·Έλ¦¬κ³ νμ¬ ν μ€νΈμμλ μ μ μΈ "5586 miles away"λΌλ ν μ€νΈλ₯Ό μ°Ύκ³ μλλ° λ§μ½μ ν μ€νΈ νλ μμΉκ° λ³κ²½λλ€λ©΄ κ±°λ¦¬κ° λ¬λΌμ§ μ μκΈ° λλ¬Έμ mock dataλ₯Ό μ¬μ©νκ±°λ ν΄λΉ λ°μ΄ν°λ₯Ό μ»μ μ μλ μμΉμ μλ κ²μ²λΌ ν μ€νΈλ₯Ό ν΄μΌν¨.
Test Organization
μμμμ²λΌ ν νλ‘μ νΈμ Unit Test, UI Testλ₯Ό μΆκ°νλ©΄ κ°κ°μ ν μ€νΈ νκ²μ΄ μΆκ°λ¨.
ν μ€νΈ νκ² λ΄λΆμλ μ¬λ¬ ν μ€νΈ ν΄λμ€κ° ν¬ν¨λκ³ , νλμ ν μ€νΈ ν΄λμ€ λ΄λΆμλ μ¬λ¬ ν μ€νΈ λ©μλκ° μ‘΄μ¬ν¨.
νμ§λ§ νλ‘μ νΈ κ΅¬μ‘°κ° λ 볡μ‘ν΄μ§λ©΄μ μλ‘μ΄ νλ μμν¬κ° μΆκ°λλ κ²½μ°, ν΄λΉ νλ μμν¬λ₯Ό μν ν μ€νΈ νκ²μ΄ μΆκ°λΌμΌνλ€.
Swift package λν μ΄λ―Έ ν μ€νΈ νκ²μ΄ μ μλΌμμ κ²μ.
Code Coverage
ν μ€νΈκ° μ€μ λ‘ λͺ¨λ μμ€ μ½λλ₯Ό μΌλ§λ μ 컀λ²νλμ§ νμΈν μ μλ λ°©λ²μ΄ λ°λ‘ μ½λ 컀λ²λ¦¬μ§.
ν μ€νΈλ₯Ό μ€ννλ λμ μμ€ μ½λμ κ° μ€μ΄ μ€νλ νμλ₯Ό μΈ‘μ νκ³ μκ°νν΄μ€.
νκ² λ΄λΆμ μ‘΄μ¬νλ νμΌ λ³λ‘ μΌλ§λ ν μ€νΈκ° μ§νλλμ§ ν λμ νμΈνλ κ²λ κ°λ₯νκ³
κ° νμΌ λ³λ‘ μ΄λ€ λΆλΆμ΄ ν μ€νΈ λλμ§, λμ§ μμλμ§, ν μ€νΈ νλ λμ κ° μ½λκ° λͺ λ²μ© μ€νλλμ§ λͺ¨λ νμΈ κ°λ₯.
Testμ μ€μμ±
- λ§μ½ μλ‘μ΄ μ½λλ₯Ό μμ±ν ν κΈ°μ‘΄ ν μ€νΈ μ½λκ° μ½λμ λ³κ²½ μ¬νμ μΆ©λΆν 컀λ²νμ§ λͺ»νλ€κ³ μκ°μ΄ λ€λ©΄ λ€μ ν μ€νΈ μ½λλ₯Ό μμ±νλ κ²μ΄ μ’μ.
- ν μ€νΈ μ½λλ μΌμ° μμ±ν μλ‘ κ·Έ κ°μΉλ₯Ό λ μ νμ©ν μ μμ.
- μμ€ μ½λμ ν¨κ» μ§νλλ ν μ€νΈλ₯Ό κ°κ³ μμμΌλ‘μ¨ μλ‘μ΄ μμ±λλ μ½λλ€μ λ μ λ’°ν μ μκ³ κΈ°λλλ‘ μλν¨μ νμΈν μ μμ.
- ν μ€νΈλ μ§μμ μΈ νλ‘μΈμ€μ΄λ©° μ±μ μνλ₯Ό μ μ§νλ λ° νμμ μ΄λ€!!
'π WWDC' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
[WWDC] Testing in Xcode: Test plans (2) | 2024.01.16 |
---|