Por que o código dentro dos testes de unidade não encontra recursos do pacote?

184

Algum código em que estou testando a unidade precisa carregar um arquivo de recurso. Ele contém a seguinte linha:

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

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

Verifiquei se isso foo.txtestá incluído na fase de criação Copy Bundle Resources do destino do teste de unidade. Por que não consigo encontrar o arquivo?

benzado
fonte

Respostas:

316

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 procure no pacote errado.) Portanto, se você adicionar um arquivo de recurso ao pacote de teste de unidade, não o encontrará se procurar no pacote principal. Se você substituir 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 está sua classe de teste de unidade e tudo ficará bem.

benzado
fonte
Nao funciona para mim. Ainda o pacote de compilação e não o pacote de teste.
31712 Chris
1
@ Chris Na linha de amostra, suponho que selfse refira a uma classe no pacote principal, não na classe de caso de teste. Substitua [self class]por qualquer classe no seu pacote principal. Vou editar meu exemplo.
benzado
@benzado O pacote ainda é o mesmo (compilação), o que está correto, eu acho. Porque quando estou usando self ou o AppDelegate, ambos estão localizados no pacote principal. Quando eu verifico as fases de compilação do destino principal, ambos os arquivos estão. Mas o que eu quero diferenciar entre o pacote principal e o teste em tempo de execução. O código em que eu preciso do pacote está no pacote principal. Eu tenho o seguinte problema. Estou carregando um arquivo png. Normalmente, esse arquivo não está no pacote principal devido ao download do usuário em um servidor. Mas, para um teste, quero usar um arquivo do pacote de teste sem copiá-lo no pacote principal.
Chris
2
@ Chris Cometi um erro na minha edição anterior e editei a resposta novamente. No momento do teste, o pacote de aplicativos ainda é o pacote principal. Se você deseja carregar um arquivo de recurso que esteja no pacote de teste de unidade, precisará usar bundleForClass:com uma classe no pacote de teste de unidade. Você deve obter o caminho do arquivo no seu código de teste de unidade e depois passar a string do caminho para o seu outro código.
benzado
Isso funciona, mas como posso distinguir entre uma implementação de execução e uma implementação de teste? Com base no fato de que, se for um teste, preciso de um recurso do pacote de teste em uma classe no pacote principal. Se for uma 'execução' regular, preciso de um recurso do pacote principal e não do pacote de teste. Qualquer ideia?
31712 Chris
80

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)

O pacote 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 de teste de unidade estará em Developer/Xcode/DerivedDataalgo como ...

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

... que é separado do Developer/CoreSimulator/Devices caminho regular do pacote configurável (sem teste de unidade) :

/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 está, por padrão, vinculado ao código do aplicativo. No entanto, o código do teste de unidade deve ter apenas a associação de destino apenas no pacote de teste. O código do aplicativo deve ter apenas a associação de destino no pacote de aplicativos. No tempo de execução, o pacote de destino do 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, a linha de comandos swift testcriará um MyProjectPackageTests.xctestpacote configurável de teste. E, o swift package generate-xcodeprojcriará um MyProjectTests.xctestpacote de teste. Esses diferentes pacotes de teste têm caminhos diferentes . Além disso, os diferentes pacotes configuráveis ​​de teste podem ter alguma estrutura interna de diretórios e diferenças de conteúdo .

Nos dois casos, o .bundlePathe .bundleURLretornará o caminho do pacote de teste atualmente em execução no macOS. No entanto, Bundleatualmente não está implementado para o Ubuntu Linux.

Além disso, a linha de comando swift builde swift testatualmente não fornece um mecanismo para copiar recursos.

No entanto, com algum esforço, é possível configurar processos para usar o Swift Package Manger com recursos nos ambientes macOS Xcode, linha de comando do macOS e linha de comando do Ubuntu. Um exemplo pode ser encontrado aqui: 004.4'2 SW Dev Swift Package Manager (SPM) com recursos Qref

Consulte também: Use recursos em testes de unidade com o Swift Package Manager

Swift Package Manager (SPM) 4.2

O Swift Package Manager PackageDescription 4.2 apresenta suporte para dependências locais .

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

Nota: Espero, mas ainda não testei, que algo como o seguinte seja 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"]
        ),
    ]
)
l --marc l
fonte
1
Também para o Swift 4, você pode usar o Bundle (para: type (of: self))
Rocket Garden
14

Com o Swift Swift 3, a sintaxe self.dynamicTypefoi preterida, use-a em vez disso

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

ou

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

Confirme se o recurso foi adicionado ao destino de teste.

insira a descrição da imagem aqui

mishimay
fonte
2
A adição de recursos ao pacote de teste torna os resultados do teste praticamente inválidos. Afinal, um recurso pode estar facilmente no destino do teste, mas não no destino do aplicativo, e todos os seus testes passariam, mas o aplicativo explodiria em chamas.
por dgatwood
1

se você tiver vários alvos em seu projeto, precisará adicionar recursos entre alvos diferentes disponíveis na Associação ao Alvo e pode ser necessário alternar entre alvos diferentes, conforme as 3 etapas mostradas na figura abaixo

insira a descrição da imagem aqui

Sultan Ali
fonte
0

Eu tive que garantir que esta caixa de seleção Teste Geral estivesse definida esta caixa de seleção Teste Geral foi definida

desenhou..
fonte