Birim testlerinin içindeki kod neden paket kaynakları bulamıyor?


184

Bazı kod Ben birim test bir kaynak dosyası yüklemek gerekiyor. Aşağıdaki satırı içerir:

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

Uygulamada iyi çalışır, ancak birim test çerçevesi tarafından çalıştırıldığında pathForResource:nil döndürür, yani bulamadı foo.txt.

Bunun birim test hedefinin Paket Kaynaklarını Kopyala oluşturma aşamasında foo.txtbulunduğundan emin oldum , bu yüzden neden dosyayı bulamıyor?

Yanıtlar:


316

Birim test kablo demeti kodunuzu çalıştırdığında, birim test paketiniz ana paket DEĞİLDİR .

Uygulamanızı değil, testleri çalıştırıyor olsanız bile, uygulama paketiniz hala ana pakettir. (Muhtemelen, bu, test ettiğiniz kodun yanlış paketi aramasını engeller.) Bu nedenle, birim test paketine bir kaynak dosyası eklerseniz, ana pakette arama yaparsanız bulamazsınız. Yukarıdaki satırı aşağıdaki ile değiştirirseniz:

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

Ardından kodunuz birim test sınıfınızın bulunduğu paketi arayacak ve her şey yoluna girecektir.


Benim için çalışmıyor. Hala test paketi değil, derleme paketi.
Chris

1
@Chris Varsaydığım örnek satırında selftest durumu sınıfına değil, ana paketteki bir sınıfa atıfta bulunuyorum. [self class]Ana paketinizdeki herhangi bir sınıfla değiştirin . Örneğimi düzenleyeceğim.
benzado

@benzado Paket hala aynı (yapı), bence doğru. Çünkü kendimi veya AppDelegate'i kullanırken, her ikisi de ana pakette bulunur. Ana hedefin Oluşturma Aşamalarını kontrol ettiğimde her iki dosya da var. Pakete ihtiyacım olan kod ana pakette. Aşağıdaki bir sorun var. Bir png dosyası yüklüyorum. Normalde, kullanıcı dosyayı sunucudan indirdiği için bu dosya ana pakette değildir. Ancak bir test için test paketindeki bir dosyayı ana pakete kopyalamadan kullanmak istiyorum.
Chris

2
@Chris Önceki düzenlememle ilgili bir hata yaptım ve cevabı tekrar düzenledim. Test zamanında, uygulama paketi hala ana pakettir. Birim test paketinde bulunan bir kaynak dosyasını yüklemek istiyorsanız, birim test paketinde bundleForClass:bir sınıfla kullanmanız gerekir . Birim yol kodunuzdaki dosyanın yolunu almalı, ardından yol dizesini diğer kodunuza geçirmelisiniz.
benzado

Bu işe yarar, ancak bir çalıştırma dağıtımı ile bir test dağıtımı arasında nasıl ayrım yapabilirim? Bir test ise, ana paketteki bir sınıfta test paketinden bir kaynağa ihtiyacım var. Düzenli bir 'run' ise, test paketinden değil ana paketten bir kaynağa ihtiyacım var. Herhangi bir fikir?
Chris

80

Bir Swift uygulaması:

Hızlı 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)

Paket, yapılandırmanız için ana ve test yollarını keşfetmenin yollarını sunar:

@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

7 | | 8 | Xcode 6'da 9, bir birim test demeti yolu olacak Developer/Xcode/DerivedDatagibi bir şey ...

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

... Developer/CoreSimulator/Devices normal (birim test dışı) paket yolundan ayrı :

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

Ayrıca, birim sınama yürütülebilir dosyasının varsayılan olarak uygulama koduyla bağlantılı olduğunu unutmayın. Ancak, birim test kodu yalnızca test paketinde yalnızca Hedef Üyeliğe sahip olmalıdır. Uygulama kodu, uygulama paketinde yalnızca Hedef Üyeliğe sahip olmalıdır. Çalışma zamanında, birim test hedef paketi yürütme için uygulama paketine enjekte edilir .

Swift Paket Yöneticisi (SPM) 4:

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

Not: Varsayılan olarak, komut satırı swift testbir MyProjectPackageTests.xctesttest paketi oluşturur. Ve, swift package generate-xcodeprojbir MyProjectTests.xctesttest paketi oluşturacak . Bu farklı test paketlerinin farklı yolları vardır . Ayrıca, farklı test paketlerinde bazı dahili dizin yapısı ve içerik farklılıkları olabilir .

Her iki durumda da, .bundlePathve .bundleURLşu anda macOS üzerinde çalıştırılan test paketinin yolunu döndürür. Ancak, Bundleşu anda Ubuntu Linux için uygulanmamıştır.

Ayrıca, komut satırı swift buildve swift testşu anda kaynakları kopyalamak için bir mekanizma sağlamaz.

Bununla birlikte, Swift Paket Yöneticisi'ni macOS Xcode, macOS komut satırı ve Ubuntu komut satırı ortamlarındaki kaynaklarla kullanmak için süreçler ayarlamak mümkündür. Bir örnek burada bulunabilir: 004.4'2 SW Dev Swift Paket Yöneticisi (SPM) ve Kaynaklar Qref

Ayrıca bkz: Swift Package Manager ile birim testlerinde kaynakları kullanma

Swift Paket Yöneticisi (SPM) 4.2

Swift Paket Yöneticisi PackageDescription 4.2 yerel bağımlılıkları destekliyor .

Yerel bağımlılıklar disk üzerinde doğrudan yolları kullanılarak yönlendirilebilen paketlerdir. Yerel bağımlılıklara yalnızca kök pakette izin verilir ve paket grafiğinde aynı ada sahip tüm bağımlılıkları geçersiz kılar.

Not: SPM 4.2 ile aşağıdakine benzer bir şeyin mümkün olmasını bekliyorum, ancak henüz test etmedim:

// swift-tools-version:4.2
import PackageDescription

let package = Package(
    name: "MyPackageTestResources",
    dependencies: [
        .package(path: "../test-resources"),
    ],
    targets: [
        // ...
        .testTarget(
            name: "MyPackageTests",
            dependencies: ["MyPackage", "MyPackageTestResources"]
        ),
    ]
)

1
Swift 4 için de Bundle'ı kullanabilirsiniz (için: type (of: self))
Rocket Garden

14

Hızlı Swift 3 ile sözdizimi self.dynamicTypekullanımdan kaldırıldı, bunun yerine bunu kullanın

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

veya

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

4

Kaynağın test hedefine eklendiğini doğrulayın.

resim açıklamasını buraya girin


2
Test paketine kaynak eklemek test sonuçlarını büyük ölçüde geçersiz kılar. Sonuçta, bir kaynak kolayca test hedefinde olabilir, ancak uygulama hedefinde olmayabilir ve testlerinizin hepsi geçecekti, ancak uygulama alevlere dönüşecekti.
dgatwood

1

projenizde birden fazla hedef varsa, Hedef Üyelikte bulunan farklı hedefler arasına kaynak eklemeniz gerekir ve aşağıdaki şekilde gösterilen 3 adımda farklı Hedef arasında geçiş yapmanız gerekebilir.

resim açıklamasını buraya girin


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.