포스트

Tip-Calculator (8)

Add image Snapshot test

Snapshot Test

간단하게 말해서 디자인 시안대로 UI를 잘 구현했는가에 대한 테스트

Snapshot Test Github

여기에 들어가면 readme에 설명이 있다.

이 라이브러리를 사용해서 테스트를 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import XCTest
import SnapshotTesting
@testable import tip_calculator

final class tip_calculatorSnapshotTests: XCTestCase {
    
    private var screenWidth: CGFloat {
        return UIScreen.main.bounds.size.width
    }
    
    func testLogoView() {
        // given
        let size = CGSize(width: screenWidth, height: 48)
        
        
        // when
        let view = LogoView()
        
        // then
        assertSnapshot(matching: view, as: .image(size: size), record: true)
        
    }
}

record를 true하면서 Logoview에 대한 이미지가 생긴다.

May-03-2024 23-19-53

CleanShot 2024-05-03 at 23 20 44@2x

이제 이사진을 가지고 비교를 하게된다..

그래서 record를 true하여 원본 사진을 남겨놔야한다.

만약 누가 logoview의 디자인을 수정했다면?

1
2
3
4
5
6
7
 private let topLabel: UILabel = {
        let label = UILabel()
        let text = NSMutableAttributedString(string: "Mr TIPs",attributes: [.font: ThemeFont.demibold(ofSize: 16)])
        text.addAttributes([.font: ThemeFont.bold(ofSize: 24)], range: NSMakeRange(3, 3)) // TIP부분 더 강조
        label.attributedText = text
        return label
    }()

Mr TIP → MR TIPs로 변경

그리고 테스트를 하면?

CleanShot 2024-05-03 at 23 24 02@2x

에러 발생.

CleanShot 2024-05-03 at 23 24 53@2x

그 경로에 있는 이미지파일을 실행해서 비교해보면?

위에 있는게 변형한것,

아래있는게 오리지널

디자인의 변화가 생겨서 Test Fail이 발생한다.

Result View Test

1
2
3
4
5
6
7
8
9
10
func testInitialResultView() {
        // given
        let size = CGSize(width: screenWidth, height: 224)

        // when
        let view = ResultView()
        
        // then
        assertSnapshot(matching: view, as: .image(size: size), record: true)
    }

이렇게 이미지를 만들어주고

record를 지우고 실행.

Tip Input View Test

1
2
3
4
5
6
7
8
9
10
func testInitialTipInputView() {
        // given
        let size = CGSize(width: screenWidth, height: 56+56+16)

        // when
        let view = TipInputView()
        
        // then
        assertSnapshot(matching: view, as: .image(size: size))
    }

Bill Input View Test

1
2
3
4
5
6
7
8
9
10
func testInitialBillInputView() {
        // given
        let size = CGSize(width: screenWidth, height: 56)

        // when
        let view = BillInputView()
        
        // then
        assertSnapshot(matching: view, as: .image(size: size))
    }

Split Input View Test

1
2
3
4
5
6
7
8
9
10
func testInitialSplitInputView() {
        // given
        let size = CGSize(width: screenWidth, height: 56)

        // when
        let view = SplitInputView()
        
        // then
        assertSnapshot(matching: view, as: .image(size: size))
    }

Custom Value로 Snapshot Test

StackOverFlow 참고

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func testResultViewWithValues() {
        // given
        let size = CGSize(width: screenWidth, height: 224)
        let result = Result(
            amountPerPerson: 100.25,
            totalBill: 45,
            totalTip: 60)
        
        // when
        let view = ResultView()
        view.configure(result: result)
        
        // then
        assertSnapshot(matching: view, as: .image(size: size), record: true)
    }

testResultViewWithValues 1

이렇게 값이 입력된 view가 생성됨.

BillInputView test

그전에 위의 스택오버플로우 사이트에서 extension을 적용

1
2
3
4
5
6
7
8
9
10
11
func testBillInputViewWithValues() {
        // given
        let size = CGSize(width: screenWidth, height: 56)

        // when
        let view = BillInputView()
        let textField = view.allSubViewsOf(type: UITextField.self).first
        textField?.text = "500"
        // then
        assertSnapshot(matching: view, as: .image(size: size))
    }

그리고

1
2
let textField = view.allSubViewsOf(type: UITextField.self).first
textField?.text = "500"

여기서 Extension을 사용하는데

testBillInputViewWithValues 1

이렇게 실제로 입력된것처럼 보인다.

TipInput View Test

1
2
3
4
5
6
7
8
9
10
11
func testTipInputViewWithValues() {
        // given
        let size = CGSize(width: screenWidth, height: 56+56+16)
        
        // when
        let view = TipInputView()
        let button = view.allSubViewsOf(type: UIButton.self).first
        button?.sendActions(for: .touchUpInside)
        // then
        assertSnapshot(matching: view, as: .image(size: size))
}

testTipInputViewWithValues 1

역시나 선택 된 것처럼 구현이 가능.

SplitInputView Test

1
2
3
4
5
6
7
8
9
10
11
func testSplitInputViewWithSelection() {
        // given
        let size = CGSize(width: screenWidth, height: 56)
        
        // when
        let view = SplitInputView()
        let button = view.allSubViewsOf(type: UIButton.self).last
        button?.sendActions(for: .touchUpInside)
        // then
        assertSnapshot(matching: view, as: .image(size: size))
    }

여기서 button의 first를 하게되면 - 버튼을 클릭하는데 숫자의 변화가 없기에 last를 선택하여 + 버튼이 클릭되는 이벤트가 보여지게 한다.

이렇게 snapshot test를 할 수 있다.

포인트는 먼저 record: true를 하고, 원본을 저장하고 이후에 ui를 재확인할때 하면 좋을듯 하다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.