티스토리 뷰

공부

@testable import에 대한 고찰

Zedd0202 2020. 7. 28. 17:00
반응형

 

안녕하세요 :) Zedd입니다.

오늘은 @testable import에 대한....고찰을 해보려고 합니다.

지금 저는 UITest를 공부중인데요...다들 그거 아셨나요?

 

UITest에서는 @testable import가 작동하지 않습니다.

결론부터 말하면

UITest는 별도의 프로세스로 앱 외부에서 실행되며, 

UI 테스트 내부에서 앱 코드에 접근 할 수 없게 "의도적으로 디자인"되었다고 합니다. 

 

그러고는 문득 아 내가..바보같이...UITest에서

응 앱 코드 접근해야돼~ 응 @testable import야~

이렇게만 생각했구나,..하고 현타가...

그리고 문득 제가 @testable import에 대해 제대로 공부해본적도 없다는 사실도 알게됨. 


Q : UITest하는데 왜 앱코드에 접근해야돼? 

A : 아 UITableView Section정의 한 enum이 있는데..그냥 하드코딩으로 Int넣기 싫어서..요 enum을 사용하려 했어요. (tmi)


 

 

그래서 @testable import에 대해 알아보려고 합니다. 

유닛 테스트를 작성하시는 분들이라면 아마 너무나도 익숙하실겁니다. 

그리고 이걸 왜하냐? 라고 생각해보면

(아마 저는) 이렇게 답하겠죠

 

A : 내 앱 타겟에 있는 internal 정의들에 접근이 안되자나...난 그걸!!!! 테스트 하고 싶은데 거기에 접근을 못하니..

@testable import "테스트를 하기 위해 필요한 앱 target이름" 

을 해주면 되니까..하는거임.. 걍..이거 하면 되던데...

 

ㅇㅇ

맞아요! 

developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/testing_with_xcode/chapters/04-writing_tests.html

 

Writing Test Classes and Methods

Writing Test Classes and Methods When you add a test target to a project with the test navigator, Xcode displays the test classes and methods from that target in the test navigator. In the test target are the test classes containing test methods. This chap

developer.apple.com

를 살펴보겠습니다. 

 

end product가 될 수 있는 앱, 번들, 프레임워크 등등이 Target일 수 있는데요. 

이 Target들은 Swift에서 별도의 module로 처리됩니다. 

(Each build target (such as an app bundle or framework) in Xcode is treated as a separate module in Swift.)

 

각각의 Target들 안에 있는 소스코드들이 public, open으로 선언되어있지 않은 이상

다른 Target에서 해당 소스에 접근을 하지 못합니다. 

 

 Testing관련 친구들

역시 각각의 Target으로 만들어집니다. 

그러니 Tests 번들 친구들이 제 App Target(위에서는 PHPicker)의 public, open이 아닌 정의들에 접근을 못하게 되죠.

 

그럼 문제가 하나 발생합니다.

테스트 코드 안에서 내 앱 타겟에 있는 소스코드들에 접근을 해야하는데

다들 아시다시피 Swift의 기본 Access level은 internal입니다.

internal은 외부에서 접근이 안되죠. 즉 Zedd라는 클래스에 접근을 못합니다.

그럼 모든 정의들에 대해서 public / open access level을 명시해줘야 할까요? 

이건 Swift의 type safety 이점을 줄이는 겁니다.

public으로 선언하는 순간 외부에서 내 타입에 마구마구 접근이 가능해지니까요.

 

이 부분을 Xcode가 해결해주는데요, 2가지 부분으로 나뉩니다. 

1. Build Setting의 Enable Testability를 Yes로 하면 Xcode는 컴파일 할 때 enable-testing flag를 넣는다고 해요.

이게 Swift의 엔티티들이 더 높은 level의 acess 권한을 가질 수 있게 됩니다. 

이렇게 되어있을 겁니다. 

2. 이제 저 "Enable Testability"를 활성화 해놓은 상태에서 import문에 @testable을 추가하면,

비로소 해당 범위에서 해당 모듈에 대한 높은 접근 권한이 활성화되게 됩니다. 

internal / public으로 표시된 클래스 및 클래스 멤버들은 마치 open인 것 마냥 표시됩니다.

(아하..public도 open으로 바뀌나봐요..!?)

헉 진짜다...@@@!!!

 

자..그래서..저는 UITest에서도 그냥 아무 생각없이

를 해줬는데요.

위 코드는 Unit Test에서는 잘 되지만, UITest에서는 빌드가 실패하게 됩니다.

대충 무ㅝ 이런 에러들임

Undefined symbols for architecture x86_64: "type metadata accessor for PHPicker.Zedd", referenced from: PHPickerUITests.PHPickerUITests.testExample() throws -> () in PHPickerUITests.o "PHPicker.Zedd.__allocating_init() -> PHPicker.Zedd", referenced from: PHPickerUITests.PHPickerUITests.testExample() throws -> () in PHPickerUITests.o ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation)

 

암튼 그래서 찾아봤더니 맨 위에서 언급한것처럼 

UITest는 별도의 프로세스로 앱 외부에서 실행되며, 

UI 테스트 내부에서 앱 코드에 접근 할 수 없게 "의도적으로 디자인"되었다고 하고, 

애초에 UITest는 앱 코드에 접근해야 하는 테스트가 아니라고 해요.

앱 코드에 접근해야 하는 테스트를 작성하려면 유닛테스트를 써라~ 라고 합니다.

 

그럼 레알루다가 방법 없는거임?

...

방법이 있긴 합니다만 따라하는건 추천하지 않습니다 :D

Target Membership에 넣어주기...!!!

그럼 뭐 @testable import 이런거 필요없어도 됩니다.

유닛 테스트에서도 마찬가지겠죠!? 

 

하지만 모든 소스코드의 Target Memebership에 UITest번들을 추가해주는 건 좀...

뗴잉....

아무튼 추천하진 않습니다. 하하

 

또 다른 방법은 그냥 사용하고 싶은 타입을 UITest쪽에 한벌 더 만드는거죠.

역시 추천하지 않습니다..

 

참고 

https://stackoverflow.com/questions/33755019/linker-error-when-accessing-application-module-in-ui-tests-in-xcode-7-1?rq=1

 

Linker error when accessing application module in UI tests in Xcode 7.1

I'm trying to implement some UI tests in my project. Everything goes fine as long as I keep it simple: record the test case, add some asserts, then run the test. This works fine, however when I try...

stackoverflow.com

https://stackoverflow.com/questions/46798225/whats-happening-behind-the-scenes-in-xctests-testable

 

What's happening behind the scenes in XCTest's @testable?

I know that @testable import MyModule gives ability to explore non-public members of MyModule from a "test" (built with "testTarget") module MyModuleTests. I need the same functionality in my "...

stackoverflow.com

반응형