Swift の実装:
スイフト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 test
を作成します。MyProjectPackageTests.xctest
そして、 はテスト バンドルswift package generate-xcodeproj
を作成します。MyProjectTests.xctest
これらの異なるテスト バンドルには異なるパスがあります。また、異なるテスト バンドルには、内部ディレクトリ構造とコンテンツの違いがある場合があります。
いずれの場合も、.bundlePath
and.bundleURL
は、現在 macOS で実行されているテスト バンドルのパスを返します。ただし、Bundle
Ubuntu Linux には現在実装されていません。
また、コマンド ラインswift build
でswift test
は現在、リソースをコピーするメカニズムを提供していません。
ただし、少し努力すれば、macOS Xcode、macOS コマンド ライン、および Ubuntu コマンド ライン環境のリソースで Swift Package Manger を使用するためのプロセスをセットアップすることができます。一例はここにあります: 004.4'2 SW Dev Swift Package Manager (SPM) With Resources Qref
関連項目: Swift Package Manager を使用して単体テストでリソースを使用する
スウィフト パッケージ マネージャー (SwiftPM) 5.3
Swift 5.3 には、「ステータス:実装済み (Swift 5.3) 」の Package Manager Resources SE-0271進化提案が含まれています。:-)
リソースは、パッケージのクライアントによる使用を常に意図しているわけではありません。リソースの 1 つの用途には、単体テストでのみ必要なテスト フィクスチャが含まれる場合があります。このようなリソースは、ライブラリ コードと共にパッケージのクライアントに組み込まれることはなく、パッケージのテストの実行中にのみ使用されます。
resources
API に新しいパラメーターをtarget
追加して、testTarget
リソース ファイルを明示的に宣言できるようにします。
SwiftPM は、ファイル システム規則を使用して、パッケージ内の各ターゲットに属するソース ファイルのセットを決定します。具体的には、ターゲットのソース ファイルは、ターゲットに指定された「ターゲット ディレクトリ」の下にあるファイルです。デフォルトでは、これはターゲットと同じ名前のディレクトリで、"Sources" (通常のターゲットの場合) または "Tests" (テスト ターゲットの場合) にありますが、この場所はパッケージ マニフェストでカスタマイズできます。
// 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"),
]),
現在の問題
Xcode
Bundle.module
SwiftPM ( Build/BuildPlan.swift SwiftTargetBuildDescription generateResourceAccessor()を参照) によって生成されるため、Xcode によってビルドされた場合、 Foundation.Bundleには存在しません。
Xcode での同等のアプローチは、Resources
参照フォルダーをモジュールに手動で追加し、Xcode ビルド フェーズを追加してディレクトリにcopy
配置し、Xcode ビルドがリソースを操作するためのコンパイラ ディレクティブを追加することです。Resource
*.bundle
#ifdef 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