programing

유닛 테스트 내부 코드가 번들 리소스를 찾을 수 없는 이유는 무엇입니까?

magicmemo 2023. 5. 6. 14:31
반응형

유닛 테스트 내부 코드가 번들 리소스를 찾을 수 없는 이유는 무엇입니까?

장치 테스트 중인 일부 코드에서 리소스 파일을 로드해야 합니다.여기에는 다음 행이 포함됩니다.

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

앱에서는 정상적으로 실행되지만 유닛 테스트 프레임워크에서 실행될 수 있습니다.pathForResource:0을 반환합니다. 즉, 찾을 수 없습니다.foo.txt.

는 그것을 .foo.txt는 장치 테스트 대상의 Copy 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/

또한 장치 테스트 실행 파일은 기본적으로 응용 프로그램 코드와 연결되어 있습니다.그러나 장치 테스트 코드는 테스트 번들에만 Target Membership이 있어야 합니다.응용 프로그램 코드는 응용 프로그램 번들에 대상 구성원 자격만 있어야 합니다.런타임에 단위 테스트 대상 번들이 실행을 위해 응용 프로그램 번들에 주입됩니다.

SPM(Swift Package Manager) 4:

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

은 " " " " 입니다.swift test를 생성합니다.MyProjectPackageTests.xctest테스트 번들그리고.swift package generate-xcodeproj를 생성합니다.MyProjectTests.xctest테스트 번들이러한 서로 다른 테스트 번들에는 서로 다른 경로가 있습니다.또한 다른 테스트 번들에는 내부 디렉터리 구조와 내용 차이가 있을 수 있습니다.

경우든, 어느경우든,든우,.bundlePath그리고..bundleURL현재 macOS에서 실행 중인 테스트 번들의 경로를 반환합니다. 하만지,Bundle은 현재 Ubuntu Linux용으로 구현되지 않았습니다.

명령행 또령줄swift build그리고.swift test현재 리소스 복사 메커니즘을 제공하지 않습니다.

그러나 약간의 노력을 기울이면 macOS Xcode, macOS 명령줄 및 Ubuntu 명령줄 환경에서 리소스와 함께 Swift Package Manager를 사용하는 프로세스를 설정할 수 있습니다.여기에서 한 가지 예를 찾을 수 있습니다. 004.4'2 SW Dev Swift Package Manager(SPM) with Resources Qref

참고 항목:Swift Package Manager를 사용한 장치 테스트에서 리소스 사용

Swift 패키지 관리자(Swift)PM) 5.3

Swift 5.3에는 Package Manager Resources SE-0271 진화 제안이 포함되어 있으며, "상태:구현(Swift 5.3)" :-)

리소스는 항상 패키지의 클라이언트가 사용하도록 설계된 것은 아닙니다. 리소스를 한 번 사용하면 장치 테스트에만 필요한 테스트 고정 장치가 포함될 수 있습니다.이러한 리소스는 라이브러리 코드와 함께 패키지의 클라이언트에 통합되지 않고 패키지의 테스트를 실행하는 동안에만 사용됩니다.

  • 새 추가resources의 매개 변수.target그리고.testTarget리소스 파일을 명시적으로 선언할 수 있는 API입니다.

SwiftPM은 패키지의 각 대상에 속하는 소스 파일 집합을 결정하기 위해 파일 시스템 규칙을 사용합니다. 특히, 대상의 소스 파일은 대상에 대해 지정된 "대상 디렉터리" 아래에 있는 파일입니다.기본적으로 이 디렉토리는 대상과 이름이 같고 "소스"(일반 대상) 또는 "테스트"(테스트 대상)에 위치하지만 이 위치는 패키지 매니페스트에서 사용자 지정할 수 있습니다.

// Get path to DefaultSettings.plist file.
let path = Bundle.module.path(forResource: "DefaultSettings", ofType: "plist")

// Load an image that can be in an asset archive in a bundle.
let image = UIImage(named: "MyIcon", in: Bundle.module, compatibleWith: UITraitCollection(userInterfaceStyle: .dark))

// Find a vertex function in a compiled Metal shader library.
let shader = try mtlDevice.makeDefaultLibrary(bundle: Bundle.module).makeFunction(name: "vertexShader")

// Load a texture.
let texture = MTKTextureLoader(device: mtlDevice).newTexture(name: "Grass", scaleFactor: 1.0, bundle: Bundle.module, options: options)

// swift-tools-version:5.3
import PackageDescription

  targets: [
    .target(
      name: "CLIQuickstartLib",
      dependencies: [],
      resources: [
        // Apply platform-specific rules.
        // For example, images might be optimized per specific platform rule.
        // If path is a directory, the rule is applied recursively.
        // By default, a file will be copied if no rule applies.
        .process("Resources"),
      ]),
    .testTarget(
      name: "CLIQuickstartLibTests",
      dependencies: [],
      resources: [
        // Copy directories as-is. 
        // Use to retain directory structure.
        // Will be at top level in bundle.
        .copy("Resources"),
      ]),

현재 문제

엑스코드

Bundle.moduleSwiftPM에 의해 생성되므로(Build/BuildPlan.swiftSwiftTargetBuildDescription generateResourceAccessor() 참조) Foundation에 존재하지 않습니다.Xcode로 빌드된 경우 번들.

Xcode에서 비교 가능한 접근법은 수동으로 추가하는 것입니다.Resources모듈에 대한 참조 폴더, Xcode 빌드 단계 추가copy말하자면Resource일부로*.bundle디렉토리, 추가#ifdef Xcode리소스와 함께 작동할 Xcode 빌드에 대한 컴파일러 지시문입니다.

#if Xcode 
extension Foundation.Bundle {
  
  /// Returns resource bundle as a `Bundle`.
  /// Requires Xcode copy phase to locate files into `*.bundle`
  /// or `ExecutableNameTests.bundle` for test resources
  static var module: Bundle = {
    var thisModuleName = "CLIQuickstartLib"
    var url = Bundle.main.bundleURL
    
    for bundle in Bundle.allBundles 
      where bundle.bundlePath.hasSuffix(".xctest") {
      url = bundle.bundleURL.deletingLastPathComponent()
      thisModuleName = thisModuleName.appending("Tests")
    }
    
    url = url.appendingPathComponent("\(thisModuleName).bundle")
    
    guard let bundle = Bundle(url: url) else {
      fatalError("Bundle.module could not load: \(url.path)")
    }
    
    return bundle
  }()
  
  /// Directory containing resource bundle
  static var moduleDir: URL = {
    var url = Bundle.main.bundleURL
    for bundle in Bundle.allBundles 
      where bundle.bundlePath.hasSuffix(".xctest") {
      // remove 'ExecutableNameTests.xctest' path component
      url = bundle.bundleURL.deletingLastPathComponent()
    }
    return url
  }()
  
}
#endif

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단계와 같이 서로 다른 대상 간에 전환해야 할 수도 있습니다.

여기에 이미지 설명 입력

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

저와 같은 사람들은 원래 게시물에서 이 점을 간과했습니다.

foo.md 이 장치 테스트 대상의 번들 리소스 복사 빌드 단계에 포함되어 있는지 확인합니다.

여기에 이미지 설명 입력

에서 파일을 찾는 코드가 있습니다: Swift의 Documents 디렉토리에 파일이 있는지 확인하는 방법은 무엇입니까?

파일이 생성되었는지 테스트하고 추가 테스트를 위해 위치를 제공하는 다음과 같은 테스트에 사용했습니다.

        let fileFound = XCTestExpectation (description: "Checking for DB File Found")
    
    
    
    let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
    let url = NSURL(fileURLWithPath: path)
    if let pathComponent = url.appendingPathComponent("filename.ext") {
        let filePath = pathComponent.path
        let fileManager = FileManager.default
        if fileManager.fileExists(atPath: filePath) {
            fileFound.fulfill()
            print("DB FILE AVAILABLE")
        } else {
            print("DB FILE NOT AVAILABLE")
        }
    } else {
        print("DB FILE PATH NOT AVAILABLE")
    }
    
    wait(for: [fileFound], timeout: 5)

이는 파일이 올바른 위치에 생성되었는지 테스트하는 것이 아니라 파일이 생성되었는지 테스트하는 것입니다.

언급URL : https://stackoverflow.com/questions/1879247/why-cant-code-inside-unit-tests-find-bundle-resources

반응형