Bibi's DevLog ๐Ÿค“๐ŸŽ

TDD, ๋‹จ์œ„ ํ…Œ์ŠคํŠธ, XCTest๋กœ iOS ์•ฑ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ๋ณธ๋ฌธ

๐Ÿ“ฑ๐ŸŽ iOS

TDD, ๋‹จ์œ„ ํ…Œ์ŠคํŠธ, XCTest๋กœ iOS ์•ฑ ํ…Œ์ŠคํŠธํ•˜๊ธฐ

๋น„๋น„ bibi 2022. 5. 9. 12:17

TDD, ๋‹จ์œ„ ํ…Œ์ŠคํŠธ

  • ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๊ฐ€ private์ด๋ฉด ๋ฌด์กฐ๊ฑด ์„ฑ๊ณต์œผ๋กœ ๋‚˜์˜ค๋‹ˆ ์กฐ์‹ฌํ•˜์ž..

https://velog.io/@minni/TDD%EC%99%80-Swift-XCTest-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0

  • TDD : Test Driven Development
    • ๊ธฐ์กด์˜ ์„ค๊ณ„ -> ๊ฐœ๋ฐœ -> ํ…Œ์ŠคํŠธ ๊ฐ€ ์•„๋‹Œ, ์„ค๊ณ„ -> ํ…Œ์ŠคํŠธ -> ๊ฐœ๋ฐœ ์˜ ์ˆœ์„œ๋กœ ์ž‘์—…
    • ์„ค๊ณ„ ๋ฌธ์ œ๋กœ ์ธํ•œ ์˜ค๋ฅ˜๋ฅผ ๋น ๋ฅด๊ฒŒ ์žก์•„๋‚ผ ์ˆ˜ ์žˆ๋‹ค.
    • TDD์˜ ์žฅ์ 
      • ๋””๋ฒ„๊น… ์‹œ๊ฐ„์„ ๋‹จ์ถ•ํ•  ์ˆ˜ ์žˆ๋‹ค.
      • ๊ฐ์ฒด์ง€ํ–ฅ์  ์„ค๊ณ„๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค. ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.
      • ํ…Œ์ŠคํŠธ๋ฅผ ์ž๋™ํ™”ํ•˜๊ณ  ํ…Œ์ŠคํŠธ ๋ฌธ์„œ๋กœ ์‚ผ์„ ์ˆ˜ ์žˆ๋‹ค.
  • Red, Green, Blue / Triple A (Arrange, Act, Assert)
    • ๋งค ๊ตฌํ˜„ ๋‹จ๊ณ„๋งˆ๋‹ค Red - Green - Blue ๋ฅผ ๋ฐ˜๋ณตํ•˜๋ฉฐ ๊ฐœ๋ฐœํ•œ๋‹ค.
    • Red : ์‹คํŒจ. ์‹คํŒจํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์ž‘์„ฑํ•˜๊ธฐ
    • Green : ์„ฑ๊ณต. ์‹คํŒจํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์„ฑ๊ณต์‹œํ‚ค๊ธฐ ์œ„ํ•œ ์ตœ์†Œํ•œ์˜ ์ฝ”๋“œ ์ž‘์„ฑํ•˜๊ธฐ
    • Blue : ๋ฆฌํŒฉํ† ๋ง. ์„ฑ๊ณตํ•œ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ๋ฆฌํŒฉํ† ๋งํ•˜๊ธฐ.
  • ๋‹จ์œ„ํ…Œ์ŠคํŠธ : Unit Test
    • ๊ธฐ๋Šฅ ๋‹จ์œ„์˜ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ.
    • ์ฆ‰ ๋ชจ๋“  ๊ธฐ๋Šฅ(๋ฉ”์„œ๋“œ)์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ์ผ€์ด์Šค๋ฅผ ์ž‘์„ฑํ•˜๋Š” ์ ˆ์ฐจ๋ฅผ ๋งํ•จ.
    • ๋‹จ์œ„ํ…Œ์ŠคํŠธ์˜ ์žฅ์ 
      • ์ฝ”๋“œ ๋ณ€๊ฒฝ์œผ๋กœ ์ธํ•œ ๋””๋ฒ„๊น…์„ ๋น ๋ฅด๊ฒŒ ํ•ด ์ค€๋‹ค.
      • ๋‹จ์œ„ํ…Œ์ŠคํŠธ๋ฅผ ๋ฏฟ๊ณ  ๋ฆฌํŒฉํ† ๋ง์„ ํŽธํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๊ณ , ๋ฆฌํŒฉํ† ๋ง ํ›„ ๋‹จ์œ„ํ…Œ์ŠคํŠธ๋กœ ์žฌ๊ฒ€์ฆ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
    • FIRST : ์ข‹์€ ๋‹จ์œ„ํ…Œ์ŠคํŠธ ์ž‘์„ฑ ์›์น™
      • Fast : ๋น ๋ฅธ. ํ…Œ์ŠคํŠธ๋Š” ๋น ๋ฅด๊ฒŒ ์‹คํ–‰๋˜์–ด์•ผ ํ•จ
      • Isolated : ๋…๋ฆฝ๋œ. ํ…Œ์ŠคํŠธ๋Š” ๋”ฐ๋กœ ์„ค์ •์ด๋‚˜ ๋ถ„๋ฆฌ๋ฅผ ํ•ด์„œ๋Š” ์•ˆ ๋จ
      • Repeatable : ๋ฐ˜๋ณต ๊ฐ€๋Šฅํ•œ. ํ…Œ์ŠคํŠธ๋ฅผ ๋ฐ˜๋ณต ์ˆ˜ํ–‰ํ•ด๋„ ๊ฐ™์€ ๊ฒฐ๊ณผ์—ฌ์•ผ ํ•œ๋‹ค.
      • Self-validating : ์ž์ฒด ๊ฒ€์ฆ. ํ…Œ์ŠคํŠธ๋Š” ์™„์ „ํžˆ ์ž๋™ํ™”๋˜์–ด์•ผ ํ•œ๋‹ค. ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ๋ฅผ ๋˜ ํ•ด์„ํ•  ํ•„์š” ์—†์ด, pass ๋˜๋Š” fail์„ ์ถœ๋ ฅํ•˜๊ฒŒ ํ•œ๋‹ค.
      • Timely : ์ ์‹œ์—. ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋Š” ๊ฐœ๋ฐœ ์ „์— ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค.

XCTest ๋กœ iOS ์•ฑ ํ…Œ์ŠคํŠธํ•˜๊ธฐ - Test Navigator

https://www.raywenderlich.com/21020457-ios-unit-testing-and-ui-testing-tutorial

  • ํ…Œ์ŠคํŠธ๋Š” ๋ฌด์—‡์„ ํ…Œ์ŠคํŠธํ•ด์•ผ ํ•˜๋Š”๊ฐ€?
    • ํ•ต์‹ฌ ๊ธฐ๋Šฅ - ๋ชจ๋ธ ํด๋ž˜์Šค๋“ค๊ณผ ๋ฉ”์„œ๋“œ๋“ค, ์ปจํŠธ๋กค๋Ÿฌ์™€์˜ ์ƒํ˜ธ์ž‘์šฉ
    • UI ์ž‘์—… ํ๋ฆ„
    • ๊ฒฝ๊ณ„ ์กฐ๊ฑด
    • ๋ฒ„๊ทธ ํ•ด๊ฒฐ

์œ ๋‹› ํ…Œ์ŠคํŠธ ๋งŒ๋“ค๊ธฐ

  1. XCode ํ”„๋กœ์ ํŠธ์—์„œ command + 6์„ ์ž…๋ ฅํ•ด ํ…Œ์ŠคํŠธ ๋„ค๋น„๊ฒŒ์ดํ„ฐ๋ฅผ ์—ฐ๋‹ค.
  2. New Unit Test Target ์„ ํƒ
    1. ํ”„๋กœ์ ํŠธ๋ช… + Tests ์ด๋ฆ„์˜ ํด๋ž˜์Šค๊ฐ€ XCTest๋ฅผ import ํ•˜๊ณ , XCTestCase๋ฅผ ์ƒ์†๋ฐ›์€ ์ฑ„๋กœ ์ƒ์„ฑ๋จ
    2. setUpWithError(), tearDownWithError() ๋ผ๋Š” ์˜ค๋ฒ„๋ผ์ด๋”ฉ ๋ฉ”์„œ๋“œ ์ž๋™ ์ƒ์„ฑ
    3. ์˜ˆ์‹œ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ์ธ testExample(), testPerformanceExample() ์ž๋™ ์ƒ์„ฑ
  3. ํ…Œ์ŠคํŠธ ์‹คํ–‰ํ•˜๊ธฐ : Command + U (์ „์ฒด ์‹คํ–‰), ๋‹ค์ด์•„๋ชฌ๋“œ ๋ฒ„ํŠผ ๋ˆ„๋ฅด๊ธฐ(๊ฐœ๋ณ„ ์‹คํ–‰)
  4. Performance Result ํ™•์ธํ•˜๊ธฐ : ํšŒ์ƒ‰ ๋‹ค์ด์•„๋ชฌ๋“œ ๋ฒ„ํŠผ ๋ˆ„๋ฅด๊ธฐ

๋ชจ๋ธ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด XCTAssert ์‚ฌ์šฉํ•˜๊ธฐ

import XCTest

@testable import PokerGameApp // ๊ธฐ๋Šฅ์„ ํ…Œ์ŠคํŠธํ•  ๋ชจ๋ธ ๊ฐ€์ ธ์˜ค๊ธฐ - ๋ชจ๋ธ์˜ ๋‚ด๋ถ€ ํƒ€์ž…๊ณผ ๋ฉ”์„œ๋“œ์— ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•จ

class PokerGameAppTests: XCTestCase {

    var systemUnderTest: PokerGame! // System Under Test

    override func setUpWithError() throws {
        // Put setup code here. This method is called before the invocation of each test method in the class.
        try super.setUpWithError()
        systemUnderTest = PokerGame(playerNames: ["A", "B"]) // ํ…Œ์ŠคํŠธํ•  ๊ฐ์ฒด ์ƒ์„ฑ
        // SUT๋Š” setUpWithError()์—์„œ ๋งŒ๋“ค๊ณ , tearDownWithError()์—์„œ ํ•ด์ œํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
    }

    override func tearDownWithError() throws {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
        systemUnderTest = nil // ํ…Œ์ŠคํŠธํ•œ ๊ฐ์ฒด ๋ฆด๋ฆฌ์ฆˆ
        try super.tearDownWithError()
    }

}
  1. import XCTest ์•„๋ž˜์— ๊ธฐ๋Šฅ์„ ํ…Œ์ŠคํŠธํ•  ๋ชจ๋ธ์„ ๊ฐ€์ ธ์˜จ๋‹ค. @testable import ๋ชจ๋ธ๋ช…

  2. PokerGameAppTests์˜ ํ”„๋กœํผํ‹ฐ๋กœ var systemUnderTest: ํ…Œ์ŠคํŠธํ•  ๊ฐ์ฒด๋ฅผ ์„ ์–ธํ•œ๋‹ค.

    1. SUT(System Under Unit) : ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ํด๋ž˜์Šค๊ฐ€ ํ…Œ์ŠคํŠธํ•  ๊ฐ์ฒด๋ฅผ ๋งํ•จ
  3. SUT๋Š” setUpWithError()์—์„œ ๋งŒ๋“ค๊ณ , tearDownWithError()์—์„œ ํ•ด์ œํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

    1. setUpWithError() ์— ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

      1. try super.setUpWithError()
        systemUnderTest = PokerGame()
      2. ํ…Œ์ŠคํŠธํ•  ๊ฐ์ฒด๋ฅผ ํด๋ž˜์Šค ์ˆ˜์ค€์—์„œ ์ƒ์„ฑํ•ด, ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค์˜ ๋ชจ๋“  ํ…Œ์ŠคํŠธ๊ฐ€ SUT ๊ฐ์ฒด์˜ ํ”„๋กœํผํ‹ฐ์™€ ๋ฉ”์„œ๋“œ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.

      3. set up = ์„ค๋ฆฝํ•˜๋‹ค

    2. tearDownWithError()์— ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

      1. systemUnderTest = nil
        try super.tearDownWithError()
      2. SUT ๊ฐ์ฒด๋ฅผ ๋ฆด๋ฆฌ์ฆˆํ•˜๋Š” ์ž‘์—…์ด๋‹ค.

      3. tear down = ํ•ด์ฒดํ•˜๋‹ค

ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์ž‘์„ฑํ•˜๊ธฐ - Given, When, Then

  • ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ ์ด๋ฆ„ : ํ•ญ์ƒ test๋กœ ์‹œ์ž‘ํ•˜๊ณ , ๋ฌด์—‡์„ ํ…Œ์ŠคํŠธํ•˜๋Š”์ง€์— ๋Œ€ํ•ด ์„ค๋ช…ํ•ด์•ผ ํ•œ๋‹ค.
  • ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๋Š” given, when, then ๋ถ€๋ถ„์œผ๋กœ ๋‚˜๋ˆ„์–ด ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
    • Given : ํ…Œ์ŠคํŠธ์— ํ•„์š”ํ•œ ๊ฐ’์„ ์„ค์ •ํ•˜๋Š” ๋ถ€๋ถ„.
    • When : ํ…Œ์ŠคํŠธํ•  ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋ถ€๋ถ„. ์˜ˆ๋ฅผ ๋“ค์–ด ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•จ
    • Then : ํ…Œ์ŠคํŠธ์— ๊ธฐ๋Œ€๋˜๋Š” ๊ฒฐ๊ด๊ฐ’์„ ๋‹จ์ •ํ•˜๋Š” ๋ถ€๋ถ„ - ์‹คํŒจ ์‹œ ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•จ.
  • ํ…Œ์ŠคํŠธ ๋””๋ฒ„๊น… : ์‹คํŒจํ•˜๋Š” ํ…Œ์ŠคํŠธ ๋””๋ฒ„๊น…ํ•˜๊ธฐ
    • ์‹คํŒจํ•˜๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ๋งŒ๋“ค์–ด ๋‘”๋‹ค.
    • Test Failure Breakpoint๋ฅผ ๋งŒ๋“ค์–ด ์–ด๋””์—์„œ ์‹คํŒจํ–ˆ๋Š”์ง€ ๋””๋ฒ„๊น…ํ•  ์ˆ˜ ์žˆ๋‹ค.
      • ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ์—์„œ ์‹คํŒจ Assertion์ด ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ํ…Œ์ŠคํŠธ ์‹คํ–‰์„ ๋ฉˆ์ถ˜๋‹ค.
    • ์ดํ›„ Xcode ๋””๋ฒ„๊ฑฐ๋กœ ํ‰์†Œ์ฒ˜๋Ÿผ ๋””๋ฒ„๊น…์„ ํ•˜๋ฉด ๋œ๋‹ค.