유닛 테스트 내부 코드가 번들 리소스를 찾을 수 없는 이유는 무엇입니까?
장치 테스트 중인 일부 코드에서 리소스 파일을 로드해야 합니다.여기에는 다음 행이 포함됩니다.
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.module
SwiftPM에 의해 생성되므로(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
'programing' 카테고리의 다른 글
ASP에서 가방을 보는 방법.NET MVC 작동 (0) | 2023.05.06 |
---|---|
PostgreSQL: 역할이 로그인할 수 없습니다. (0) | 2023.05.06 |
ASP에 필요한 확인란을 만들려면 어떻게 해야 합니까?NET 양식? (0) | 2023.05.06 |
Meteor가 실행되는 동안 다른 클라이언트에서 Meteor의 MongoDB에 액세스하려면 어떻게 해야 합니까? (0) | 2023.05.06 |
목표-C: id와 void의 차이 * (0) | 2023.05.06 |