Mengapa kode di dalam unit test tidak dapat menemukan sumber daya bundel?

184

Beberapa kode saya unit pengujian perlu memuat file sumber daya. Ini berisi baris berikut:

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

Dalam aplikasi itu berjalan dengan baik, tetapi ketika dijalankan oleh kerangka pengujian unit pathForResource:mengembalikan nihil, artinya tidak bisa menemukan foo.txt.

Saya telah memastikan bahwa foo.txtitu termasuk dalam tahap pembuatan Salin Kumpulan Sumber Daya dari target pengujian unit, jadi mengapa tidak dapat menemukan file?

benzado
sumber

Jawaban:

316

Ketika harness uji unit menjalankan kode Anda, bundel uji unit Anda BUKAN bundel utama.

Meskipun Anda menjalankan tes, bukan aplikasi Anda, bundel aplikasi Anda masih bundel utama. (Agaknya, ini mencegah kode yang Anda uji dari mencari bundel yang salah.) Jadi, jika Anda menambahkan file sumber ke bundel unit test, Anda tidak akan menemukannya jika mencari bundel utama. Jika Anda mengganti baris di atas dengan:

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

Kemudian kode Anda akan mencari bundel tempat kelas uji unit Anda berada, dan semuanya akan baik-baik saja.

benzado
sumber
Tidak bekerja untuk saya. Masih bundel build dan bukan bundel uji.
Chris
1
@ Chris Dalam baris sampel saya mengasumsikan selfmengacu pada kelas di bundel utama, bukan kelas kasus uji. Ganti [self class]dengan kelas apa pun di bundel utama Anda. Saya akan mengedit contoh saya.
benzado
@ Benzado Bundelnya masih sama (build), yang menurut saya benar. Karena ketika saya menggunakan self atau AppDelegate, keduanya terletak di bundel utama. Ketika saya memeriksa Fase Build dari target utama kedua file. Tapi apa yang saya ingin berbeda antara bundel utama dan test pada saat run time. Kode di mana saya perlu bundel ada di bundel utama. Saya memiliki masalah berikut. Saya memuat file png. Biasanya file ini tidak ada dalam bundel utama karena pengguna mengunduhnya dari server. Tetapi untuk pengujian saya ingin menggunakan file dari bundel tes tanpa menyalinnya ke bundel utama.
Chris
2
@ Chris Saya membuat kesalahan dengan hasil edit saya sebelumnya, dan mengedit jawabannya lagi. Pada waktu pengujian, bundel aplikasi masih merupakan bundel utama. Jika Anda ingin memuat file sumber daya yang ada dalam bundel pengujian unit, Anda perlu menggunakan bundleForClass:kelas dalam bundel pengujian unit. Anda harus mendapatkan path file dalam kode tes unit Anda, kemudian meneruskan string path ke kode Anda yang lain.
benzado
Ini berfungsi tetapi bagaimana saya bisa membedakan antara run-deploy dan test-deploy? Berdasarkan fakta jika itu adalah tes saya membutuhkan sumber daya dari bundel tes di kelas dalam bundel utama. Jika ini adalah 'run' biasa, saya membutuhkan sumber daya dari bundel utama dan bukan bundel uji. Ada ide?
Chris
80

Implementasi Swift:

Cepat 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)

Bundle menyediakan cara untuk menemukan jalur utama dan uji untuk konfigurasi Anda:

@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

Dalam Xcode 6 | 7 | 8 | 9, jalur bundel unit-test akan berada dalam Developer/Xcode/DerivedDatasesuatu seperti ...

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

... yang terpisah dari Developer/CoreSimulator/Devices jalur bundel (non-unit-test) biasa :

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

Perhatikan juga bahwa unit test executable, secara default, terhubung dengan kode aplikasi. Namun, kode tes unit hanya boleh memiliki Keanggotaan Target hanya di bundel tes. Kode aplikasi seharusnya hanya memiliki Keanggotaan Target dalam bundel aplikasi. Saat runtime, bundel target uji unit disuntikkan ke bundel aplikasi untuk dieksekusi .

Manajer Paket Swift (SPM) 4:

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

Catatan: Secara default, baris perintah swift testakan membuat MyProjectPackageTests.xctestbundel uji. Dan, swift package generate-xcodeprojakan membuat MyProjectTests.xctestbundel uji. Kumpulan uji yang berbeda ini memiliki jalur yang berbeda . Juga, kumpulan uji yang berbeda mungkin memiliki beberapa struktur direktori internal dan perbedaan konten .

Dalam kedua kasus tersebut, .bundlePathdan .bundleURLakan mengembalikan jalur bundel pengujian yang saat ini sedang dijalankan di macOS. Namun, Bundlesaat ini tidak diterapkan untuk Ubuntu Linux.

Juga, baris perintah swift builddan swift testsaat ini tidak menyediakan mekanisme untuk menyalin sumber daya.

Namun, dengan beberapa upaya, dimungkinkan untuk mengatur proses untuk menggunakan Swift Package Manger dengan sumber daya di macOS Xcode, baris perintah macOS, dan lingkungan baris perintah Ubuntu. Salah satu contoh dapat ditemukan di sini: 004.4'2 SW Dev Swift Package Manager (SPM) Dengan Resources Qref

Lihat juga: Gunakan sumber daya dalam pengujian unit dengan Swift Package Manager

Manajer Paket Swift (SPM) 4.2

Package Manager Swift PackageDescription 4.2 memperkenalkan dukungan dependensi lokal .

Ketergantungan lokal adalah paket pada disk yang dapat dirujuk langsung menggunakan jalurnya. Ketergantungan lokal hanya diperbolehkan dalam paket root dan menimpa semua dependensi dengan nama yang sama dalam grafik paket.

Catatan: Saya perkirakan, tetapi belum diuji, bahwa sesuatu seperti yang berikut ini dimungkinkan dengan 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"]
        ),
    ]
)
l --marc l
sumber
1
Untuk Swift 4 juga, Anda dapat menggunakan Bundle (untuk: tipe (dari: diri))
Rocket Garden
14

Dengan Swift 3, sintaks self.dynamicTypetelah ditinggalkan, gunakan ini sebagai gantinya

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

atau

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

Konfirmasikan bahwa sumber daya ditambahkan ke target pengujian.

masukkan deskripsi gambar di sini

mishimay
sumber
2
Menambahkan sumber daya ke bundel pengujian membuat hasil tes sebagian besar tidak valid. Lagi pula, sumber daya bisa dengan mudah berada dalam target pengujian tetapi tidak dalam target aplikasi, dan semua tes Anda akan berlalu, tetapi aplikasi tersebut akan terbakar.
dgatwood
1

jika Anda memiliki banyak target dalam proyek Anda maka Anda perlu menambahkan sumber daya di antara berbagai target yang tersedia di Keanggotaan Target dan Anda mungkin perlu beralih di antara Target yang berbeda sebagai 3 langkah yang ditunjukkan pada gambar di bawah ini

masukkan deskripsi gambar di sini

Sultan Ali
sumber
0

Saya harus memastikan bahwa kotak centang Pengujian Umum ini ditetapkan kotak centang Pengujian Umum ini telah ditetapkan

menggambar ..
sumber