[cocoa] 단위 테스트 내부에서 코드가 번들 자원을 찾을 수없는 이유는 무엇입니까?

단위 테스트중인 일부 코드는 리소스 파일을로드해야합니다. 다음 줄이 포함되어 있습니다.

NSString *path = [[NSBundle mainBundle] pathForResource:@"foo" ofType:@"txt"];

응용 프로그램에서는 제대로 실행되지만 단위 테스트 프레임 워크 pathForResource:로 실행하면 nil을 반환하여 찾을 수 없음을 의미합니다 foo.txt.

유닛 테스트 대상 foo.txtCopy Bundle Resources 빌드 단계에 포함되어 있는지 확인했는데 왜 파일을 찾을 수 없습니까?



답변

단위 테스트 장치가 코드를 실행할 때 단위 테스트 번들이 기본 번들 이 아닙니다 .

애플리케이션이 아닌 테스트를 실행하더라도 애플리케이션 번들은 여전히 ​​기본 번들입니다. (이것은 아마도 테스트중인 코드가 잘못된 번들을 검색하지 못하게합니다.) 따라서 단위 테스트 번들에 자원 파일을 추가하면 기본 번들을 검색해도 찾을 수 없습니다. 위의 줄을 다음과 같이 바꾸면 :

NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSString *path = [bundle pathForResource:@"foo" ofType:@"txt"];

그런 다음 코드는 단위 테스트 클래스가있는 번들을 검색하고 모든 것이 잘됩니다.


답변

스위프트 구현 :

스위프트 2

let testBundle = NSBundle(forClass: self.dynamicType)
let fileURL = testBundle.URLForResource("imageName", withExtension: "png")
XCTAssertNotNil(fileURL)

스위프트 3, 스위프트 4

let testBundle = Bundle(for: type(of: self))
let filePath = testBundle.path(forResource: "imageName", ofType: "png")
XCTAssertNotNil(filePath)

번들은 구성의 기본 및 테스트 경로를 발견하는 방법을 제공합니다.

@testable import Example

class ExampleTests: XCTestCase {

    func testExample() {
        let bundleMain = Bundle.main
        let bundleDoingTest = Bundle(for: type(of: self ))
        let bundleBeingTested = Bundle(identifier: "com.example.Example")!

        print("bundleMain.bundlePath : \(bundleMain.bundlePath)")
        // …/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Xcode/Agents
        print("bundleDoingTest.bundlePath : \(bundleDoingTest.bundlePath)")
        // …/PATH/TO/Debug/ExampleTests.xctest
        print("bundleBeingTested.bundlePath : \(bundleBeingTested.bundlePath)")
        // …/PATH/TO/Debug/Example.app

        print("bundleMain = " + bundleMain.description) // Xcode Test Agent
        print("bundleDoingTest = " + bundleDoingTest.description) // Test Case Bundle
        print("bundleUnderTest = " + bundleBeingTested.description) // App Bundle

Xcode 6 | 7 | 8 | 9에서 단위 테스트 번들 경로Developer/Xcode/DerivedData다음과 같습니다.

/Users/
  UserName/
    Library/
      Developer/
        Xcode/
          DerivedData/
            App-qwertyuiop.../
              Build/
                Products/
                  Debug-iphonesimulator/
                    AppTests.xctest/
                      foo.txt

Developer/CoreSimulator/Devices 일반 (비 테스트) 번들 경로 와 별도입니다 .

/Users/
  UserName/
    Library/
    Developer/
      CoreSimulator/
        Devices/
          _UUID_/
            data/
              Containers/
                Bundle/
                  Application/
                    _UUID_/
                      App.app/

또한 단위 테스트 실행 파일은 기본적으로 응용 프로그램 코드와 연결되어 있습니다. 그러나 단위 테스트 코드는 테스트 번들에만 대상 멤버십이 있어야합니다. 응용 프로그램 코드는 응용 프로그램 번들에 대상 멤버 자격 만 있어야합니다. 런타임시, 유닛 테스트 대상 번들은 실행을 위해 애플리케이션 번들에 주입됩니다 .

스위프트 패키지 관리자 (SPM) 4 :

let testBundle = Bundle(for: type(of: self)) 
print("testBundle.bundlePath = \(testBundle.bundlePath) ")

참고 : 기본적으로 명령 행 swift testMyProjectPackageTests.xctest테스트 번들 을 작성합니다 . 그리고 테스트 번들 swift package generate-xcodeproj을 만듭니다 MyProjectTests.xctest. 이러한 다른 테스트 번들에는 다른 경로가 있습니다. 또한 다른 테스트 번들은 내부 디렉토리 구조 및 내용 차이 가있을 수 있습니다 .

두 경우 모두, .bundlePath그리고 .bundleURL현재 맥 OS에서 실행되는 테스트 번들의 경로를 반환합니다. 그러나 Bundle현재 Ubuntu Linux에는 구현되어 있지 않습니다.

또한, 명령 줄 swift buildswift test현재 자원을 복사하는 메커니즘을 제공하지 않습니다.

그러나 약간의 노력으로 macOS Xcode, macOS 명령 행 및 Ubuntu 명령 행 환경의 자원과 함께 Swift Package Manger를 사용하기위한 프로세스를 설정할 수 있습니다. 한 가지 예는 여기에서 찾을 수 있습니다 : 004.4’2 SW 개발자 스위프트 패키지 관리자 (SPM)을 자원 Qref으로

참조 : 사용 자원을 스위프트 패키지 관리자와 단위 테스트에

스위프트 패키지 관리자 (SPM) 4.2

Swift Package Manager PackageDescription 4.2 에는 로컬 종속성 지원이 도입되었습니다 .

로컬 종속성은 경로를 사용하여 직접 참조 할 수있는 디스크의 패키지입니다. 로컬 종속성은 루트 패키지에서만 허용되며 패키지 그래프에서 이름이 같은 모든 종속성을 재정의합니다.

참고 : SPM 4.2에서는 다음과 같은 것이 가능해야하지만 아직 테스트하지는 않았습니다.

// swift-tools-version:4.2
import PackageDescription

let package = Package(
    name: "MyPackageTestResources",
    dependencies: [
        .package(path: "../test-resources"),
    ],
    targets: [
        // ...
        .testTarget(
            name: "MyPackageTests",
            dependencies: ["MyPackage", "MyPackageTestResources"]
        ),
    ]
)


답변

swift Swift 3에서는 구문 self.dynamicType이 더 이상 사용되지 않습니다. 대신 이것을 사용하십시오

let testBundle = Bundle(for: type(of: self))
let fooTxtPath = testBundle.path(forResource: "foo", ofType: "txt")

또는

let fooTxtURL = testBundle.url(forResource: "foo", withExtension: "txt")


답변

자원이 테스트 대상에 추가되었는지 확인하십시오.

여기에 이미지 설명을 입력하십시오


답변

프로젝트에 여러 대상이있는 경우 대상 멤버십 에서 사용 가능한 다른 대상간에 자원을 추가 해야하며 아래 그림에 표시된 3 단계로 다른 대상 간에 전환해야 할 수도 있습니다.

여기에 이미지 설명을 입력하십시오


답변

이 일반 테스트 확인란이 설정되어 있는지 확인해야합니다 이 일반 테스트 확인란이 설정되었습니다


답변