私が単体テストしているコードの中には、リソースファイルをロードする必要があるものがあります。次の行が含まれています。
NSString *path = [[NSBundle mainBundle] pathForResource:@"foo" ofType:@"txt"];
アプリでは正常に実行されますが、単体テストフレームワークによって実行される場合はpathForResource:
はnilを返します。つまり、foo.txt
。
foo.txt
は、単体テストターゲットのバンドルリソースのコピービルドフェーズに含まれているのに、なぜファイルが見つからないのですか?
ユニットテストハーネスがコードを実行すると、ユニットテストバンドルは[〜#〜] not [〜#〜]メインバンドルになります。
アプリケーションではなくテストを実行している場合でも、アプリケーションバンドルは依然としてメインバンドルです。 (おそらく、これにより、テストしているコードが間違ったバンドルを検索するのを防ぐことができます。)したがって、ユニットテストバンドルにリソースファイルを追加すると、メインバンドルを検索しても検索されません。上記の行を次のように置き換えた場合:
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSString *path = [bundle pathForResource:@"foo" ofType:@"txt"];
その後、コードはユニットテストクラスが含まれているバンドルを検索し、すべてが正常に実行されます。
A Swift=実装:
Swift 2
let testBundle = NSBundle(forClass: self.dynamicType)
let fileURL = testBundle.URLForResource("imageName", withExtension: "png")
XCTAssertNotNil(fileURL)
Swift 3、Swift 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では、unit-test bundle pathは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/
また、単体テスト実行可能ファイルは、デフォルトでアプリケーションコードとリンクされていることに注意してください。ただし、単体テストコードには、テストバンドルのみにターゲットメンバーシップが含まれている必要があります。アプリケーションコードには、アプリケーションバンドルにターゲットメンバーシップのみが含まれている必要があります。実行時、単体テストのターゲットバンドルは 実行のためにアプリケーションバンドルに挿入されます 。
Swift Package Manager(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
および.bundleURL
は、macOSで現在実行されているテストバンドルのパスを返します。ただし、Bundle
は現在Ubuntu Linuxには実装されていません。
また、コマンドラインSwift build
およびSwift test
は現在、リソースをコピーするメカニズムを提供していません。
ただし、多少の努力を払えば、macOS Xcode、macOSコマンドライン、Ubuntuコマンドライン環境のリソースでSwift Package Mangerを使用するためのプロセスを設定できます。1つの例を見つけることができます。ここ: 4.4'2 SW Dev SwiftリソースQrefを使用したパッケージマネージャー(SPM)
参照: Swift Package Manager)を使用した単体テストでのリソースの使用
Swift Package Manager(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"]
),
]
)
With 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")