DEV Community

Cover image for Testando membros privados com Private Imports
Patrick Lorran
Patrick Lorran

Posted on • Updated on

Testando membros privados com Private Imports

Escrever testes unitários pode ser uma tarefa desafiadora quando o objeto a ser testado possui propriedades ou métodos privados. Neste cenário o uso de mocks não é aplicável e uma solução recorrente é escrever propriedades ou métodos adicionais para validar os membros privados, poluindo o objeto. Neste artigo trago uma solução alternativa que envolve o uso de flags de compilação e atributos privados da linguagem para acessar os membros privados somente no ambiente de teste.

Configuração Inicial

O primeiro passo é adicionar a flag -enable-private-imports ao seu pacote ou aplicação. Essa flag instrui o compilador a expor membros internos e privados desde que sejam explicitados no código-fonte (falaremos disso mais adiante). No seu arquivo de pacote adicione o parâmetro swiftSettings à descrição do target e utilize o método unsafeFlags(_:_:) para inserir a flag mencionada.

.target(
  name: "MyAwesomeLibrary",
  dependencies: [
    ...
  ],
  swiftSettings: [
    .unsafeFlags(["-enable-private-imports"], .when(configuration: .debug))
  ])
Enter fullscreen mode Exit fullscreen mode

Neste exemplo restringimos a flag à configuração debug pois não queremos que nosso código de produção exponha os membros privados. Basta utilizar um objeto do tipo BuildSettingCondition para especificar as condições sob as quais as flags de compilação serão aplicadas.

Caso queira expor os membros privados da sua aplicação, adicione a flag na opção Other Swift Flags em Swift Compiler - Custom Flags na aba Build Settings do projeto.

Implementação

Suponhamos que nosso pacote tenha o seguinte código no arquivo Operations.swift:

public struct Operations {

  private var count: Int = 0

  public func sum(_ x: Int, _ y: Int) -> Int {
    count += 1
    return x + y
  }
}
Enter fullscreen mode Exit fullscreen mode

A variável count registra o número de operações de soma realizadas — ou seja, a quantidade de vezes que o método sum(_:_:) foi invocado. Vamos, agora, escrever testes unitários para nosso objeto.

import XCTest

@testable import MyAwesomeLibrary

final class OperationsTests: XCTestCase {

  private var operations: Operations!

  override func setup() {
    super.setup()

    operations = .init()
  }

  func testSum() {
    let result = operations.sum(2, 3)
    XCTAssertEqual(result, 5)
  }
}
Enter fullscreen mode Exit fullscreen mode

O método testSum() apenas confere se a operação de soma do nosso objeto está correta. Para podermos testar se a variável count está sendo incrementada a cada chamada temos que expô-la para nossa classe de teste. Para isso adicionaremos o atributo privado @_private(sourceFile:) à nossa diretiva de importação passando como argumento o nome do arquivo que contém o código-fonte dos membros privados a serem expostos.

import XCTest

@_private(sourceFile: "Operations.swift")
@testable import MyAwesomeLibrary

final class OperationsTests: XCTestCase {

  private var operations: Operations!

  override func setup() {
    super.setup()

    operations = .init()
  }

  func testSum() {
    let result = operations.sum(2, 3)

    XCTAssertEqual(result, 5)
  }

  func testCount() {
    let _ = operations.sum(2, 3)
    let _ = operations.sum(4, 5)

    XCTAssertEqual(operations.count, 2)
  }
}
Enter fullscreen mode Exit fullscreen mode

O método testCount() verifica se após duas chamadas de sum(_:_:) a variável count é incrementada duas vezes. A assertiva acessa a propriedade count do objeto operations sem causar um erro de compilação graças ao atributo @_private(sourceFile: "Operations.swift") que inserimos ao importar o módulo.

Dessa forma, sempre que precisar testar membros privados de determinado objeto, importe o módulo com o atributo @_private(sourceFile:) antes e passe como argumento o nome do arquivo que contém esse objeto.

Conclusão

A exposição de membros privados pode facilitar muito a escrita dos testes unitários por eliminar a necessidade de membros auxiliares que de nada servem a não ser observar outros membros. Apenas se atente para que a flag não seja aplicada à compilação de release a fim de evitar a exposição desnecessária de membros privados.

Top comments (0)