Por que o código dentro dos testes de unidade não pode encontrar resources de pacote?

Algum código que eu sou teste de unidade precisa carregar um arquivo de recurso. Contém a seguinte linha:

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

No aplicativo, ele é executado muito bem, mas quando executado pela estrutura de teste de unidade pathForResource: retorna nil, o que significa que não foi possível localizar foo.txt .

Certifiquei-me de que foo.txt está incluído na fase de compilation Copy Bundle Resources do destino de teste de unidade, então por que não pode encontrar o arquivo?

Quando o equipamento de teste de unidade executa seu código, seu pacote de teste de unidade NÃO é o pacote principal.

Mesmo que você esteja executando testes, não seu aplicativo, seu pacote de aplicativos ainda é o pacote principal. (Presumivelmente, isso impede que o código que você está testando pesquise no pacote errado.) Portanto, se você adicionar um arquivo de resources ao pacote de teste de unidade, não o encontrará se pesquisar o bundle principal. Se você replace a linha acima por:

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

Em seguida, seu código pesquisará o pacote em que sua class de teste de unidade está e tudo ficará bem.

Uma implementação rápida:

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) 

Bundle fornece maneiras de descobrir os caminhos principais e de teste para sua configuração:

 @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 

No Xcode 6 | 7 | 8 | 9, um caminho de pacote unit-test estará em Developer/Xcode/DerivedData algo como …

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

… que é separado do caminho do pacote Developer/CoreSimulator/Devices regular (non-unit-test) do Developer/CoreSimulator/Devices :

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

Observe também que o executável do teste de unidade é, por padrão, vinculado ao código do aplicativo. No entanto, o código de teste de unidade deve ter apenas a participação de destino apenas no pacote de teste. O código do aplicativo deve ter apenas uma Associação de destino no pacote de aplicativos. No tempo de execução, o pacote de destino de teste de unidade é injetado no pacote de aplicativos para execução .

Gerenciador de Pacotes Swift (SPM) 4:

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

Nota: Por padrão, o swift test linha de comando criará um pacote de teste MyProjectPackageTests.xctest . E o swift package generate-xcodeproj criará um pacote de teste MyProjectTests.xctest . Esses pacotes de teste diferentes possuem caminhos diferentes . Além disso, os diferentes pacotes de teste podem ter algumas estruturas de diretórios internos e diferenças de conteúdo .

Em qualquer um dos casos, o .bundlePath e o .bundleURL retornarão o caminho do pacote de testes sendo executado no macOS. No entanto, o Bundle não está atualmente implementado para o Ubuntu Linux.

Além disso, a linha de comando swift build e swift test não fornecem atualmente um mecanismo para copiar resources.

No entanto, com algum esforço, é possível configurar processos para usar o Gerenciador de Pacotes Swift com resources no MacOS Xcode, na linha de comando do macOS e nos ambientes de linha de comando do Ubuntu. Um exemplo pode ser encontrado aqui: 004.4’2 Gerente de pacote SW Dev Swift (SPM) com resources Qref

Veja também: Use resources em testes unitários com o Swift Package Manager

Gerenciador de Pacotes Swift (SPM) 4.2

O Swift Package Manager PackageDescription 4.2 introduz o suporte de dependencies locais .

Dependências locais são pacotes em disco que podem ser referenciados diretamente usando seus caminhos. As dependencies locais só são permitidas no pacote raiz e substituem todas as dependencies com o mesmo nome no gráfico do pacote.

Nota: Espero, mas ainda não testei, que algo como o seguinte deve ser possível com o 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"] ), ] ) 

Com o swift Swift 3, a syntax self.dynamicType foi descontinuada. Use essa syntax

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

ou

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

Confirme se o recurso é adicionado ao destino de teste.

insira a descrição da imagem aqui

Se você tem múltiplos alvos em seu projeto, então você precisa adicionar resources entre alvos diferentes disponíveis na Associação Alvo e você pode precisar alternar entre Alvos diferentes conforme os 3 passos mostrados na figura abaixo.

insira a descrição da imagem aqui