Skip to content

Commit c240314

Browse files
committed
add post
1 parent 3352947 commit c240314

File tree

1 file changed

+108
-0
lines changed

1 file changed

+108
-0
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
---
2+
layout: post
3+
title: "[SwiftTesting] Swift Testing 분석 (1) - Expand Macro(Test)"
4+
tags: [swift, testing, Swift Testing]
5+
---
6+
{% include JB/setup %}
7+
8+
Xcode 16이 출시되면서 새로운 테스트 패키지인 [Swift Testing](https://github.com/swiftlang/swift-testing)이 추가되었습니다. 기존 XCTest를 이용하여 테스트를 작성했다면, 이제는 Swift Testing을 활용하여 현대적인 테스트 케이스를 작성할 수 있게 되었습니다.
9+
10+
```swift
11+
import Testing
12+
13+
struct SampleTest {
14+
init() {}
15+
16+
@Test("Hello 테스트")
17+
func hello() {
18+
#expect(true)
19+
}
20+
21+
@Test("World 테스트")
22+
func world() {
23+
#expect(false != true)
24+
}
25+
}
26+
```
27+
28+
XCTest를 사용할 때는 함수명이 곧 테스트 케이스 이름이었지만, 이제는 Test 매크로에 표시할 이름을 넣을 수 있게 되었습니다.
29+
30+
현대적인 방식으로 테스트 케이스를 작성할 수 있게 되었지만, Xcode는 어떻게 `@Test` 매크로가 붙어 있는 함수를 찾아서 수행하는 것일까요?
31+
32+
`@Test`에서 Expand Macro를 실행하여 어떻게 코드가 생성되는지 확인할 수 있습니다.
33+
34+
<br/>
35+
<p style="text-align:center;">
36+
<img src="{{ site.prod_url }}/image/2024/12/01.png"/>
37+
</p><br/>
38+
39+
```swift
40+
@available(*, deprecated, message: "This function is an implementation detail of the testing library. Do not use it directly.")
41+
@Sendable private static func $s18SampleLibraryTests0A4TestV5hello0D0fMp_11funchello__fMu0_() async throws -> Void {
42+
try await Testing.__ifMainActorIsolationEnforced { [] in
43+
let $s18SampleLibraryTests0A4TestV5hello0D0fMp_11funchello__fMu_ = try await (SampleTest(), Testing.__requiringTry, Testing.__requiringAwait).0
44+
_ = try await ($s18SampleLibraryTests0A4TestV5hello0D0fMp_11funchello__fMu_.hello(), Testing.__requiringTry, Testing.__requiringAwait).0
45+
} else: { [] in
46+
let $s18SampleLibraryTests0A4TestV5hello0D0fMp_11funchello__fMu_ = try await (SampleTest(), Testing.__requiringTry, Testing.__requiringAwait).0
47+
_ = try await ($s18SampleLibraryTests0A4TestV5hello0D0fMp_11funchello__fMu_.hello(), Testing.__requiringTry, Testing.__requiringAwait).0
48+
}
49+
}
50+
51+
@available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.")
52+
enum $s18SampleLibraryTests0A4TestV5hello0D0fMp_41__🟠$test_container__function__funchello__fMu_: Testing.__TestContainer {
53+
static var __tests: [Testing.Test] {
54+
get async {
55+
return [
56+
.__function(
57+
named: "hello()",
58+
in: SampleTest.self,
59+
xcTestCompatibleSelector: nil,
60+
displayName: "Hello 테스트", traits: [], sourceLocation: Testing.SourceLocation(fileID: "SampleLibraryTests/SampleLibraryTests.swift", filePath: "/Users/minsone/tmp/20241216/SampleLibrary/Tests/SampleLibraryTests/SampleLibraryTests.swift", line: 9, column: 6),
61+
parameters: [],
62+
testFunction: $s18SampleLibraryTests0A4TestV5hello0D0fMp_11funchello__fMu0_
63+
)
64+
]
65+
}
66+
}
67+
}
68+
```
69+
70+
`$s18SampleLibraryTests0A4TestV5hello0D0fMp_11funchello__fMu0_` 함수와 `$s18SampleLibraryTests0A4TestV5hello0D0fMp_41__🟠$test_container__function__funchello__fMu_` enum이 만들어졌습니다.<br/>
71+
72+
여기에서 우리는 함수 이름이 Mangling 되어 있다는 것을 알 수 있습니다. [Wikipedia - Name mangling](https://en.wikipedia.org/wiki/Name_mangling#Swift), [Swift - Mangling](https://github.com/swiftlang/swift/blob/main/docs/ABI/Mangling.rst), [Name Mangling](https://minsone.github.io/programming/swift-name-mangling)
73+
74+
이 함수 이름을 Demangle 해봅시다.
75+
76+
```shell
77+
$ xcrun swift-demangle s18SampleLibraryTests0A4TestV5hello0D0fMp_11funchello__fMu0_
78+
$s18SampleLibraryTests0A4TestV5hello0D0fMp_11funchello__fMu0_ ---> unique name #2 of funchello__ in peer macro @Test expansion #1 of hello in SampleLibraryTests.SampleTest
79+
```
80+
81+
`s` 는 Swift 심볼을 의미, `18SampleLibraryTests``SampleLibraryTests` 모듈 이름 및 모듈 이름 글자수인 18자, `0A4TestV`는 Test 라는 Value 타입인 구조체, `5hello0D0`는 메서드나 속성 이름을 나타냅니다.
82+
83+
<br/>
84+
85+
다음으로 enum 코드를 살펴보면, 특이하게 `🟠` 이모지가 들어있는 것을 확인할 수 있습니다. 왜 이런 이모지가 들어있는 것일까요? 알아보기 위해 [Swift Testing](https://github.com/swiftlang/swift-testing) 라이브러리를 살펴봅시다.
86+
87+
<br/>
88+
<p style="text-align:center;">
89+
<img src="{{ site.prod_url }}/image/2024/12/02.png"/>
90+
</p>
91+
92+
[GitHub 검색 결과](https://github.com/search?q=repo%3Aswiftlang%2Fswift-testing%20%F0%9F%9F%A0&type=code)
93+
94+
<br/>
95+
96+
검색을 통해 [TestDeclarationMacro](https://github.com/swiftlang/swift-testing/blob/e2ec0411e5f7407fc2d325c9feea8f0ac10a60e2/Sources/TestingMacros/TestDeclarationMacro.swift#L467) 매크로가 `__🟠$test_container__function__` 문자열을 붙여준다는 것을 확인할 수 있으며, [Test+Discovery.swift](https://github.com/swiftlang/swift-testing/blob/e2ec0411e5f7407fc2d325c9feea8f0ac10a60e2/Sources/Testing/Test%2BDiscovery.swift#L26) 에서 `__🟠$test_container__` 문자열로 무엇인가 발견하려는 것을 알 수 있습니다.
97+
98+
<br/>
99+
100+
다음 편에서 `Test+Discovery.swift` 코드부터 살펴보면서 어떻게 테스트 케이스를 찾아서 실행하는지 살펴보겠습니다.
101+
102+
<br/>
103+
104+
## 참고자료
105+
106+
* [Swift Testing](https://github.com/swiftlang/swift-testing)
107+
* [Displaying all SwiftUI Previews in a Storybook app](https://medium.com/eureka-engineering/displaying-all-swiftui-previews-in-a-storybook-app-1dd8e925d777)
108+
* [eure/Storybook-ios](https://github.com/eure/Storybook-ios)

0 commit comments

Comments
 (0)