Guia Definitivo de Testes no iOS: Do Unitário ao CI/CD Frameworks: Swift Testing, XCTest, SwiftData, XCUITest


Este é um guia de estudos sobre testes no ecossistema da Apple. Se você já construiu um aplicativo, sabe que garantir que ele continue funcionando perfeitamente a cada nova atualização, refatoração ou adição de funcionalidade é o verdadeiro desafio. É exatamente para resolver esse problema que utilizamos os testes automatizados. Em sua essência, um teste automatizado é simplesmente um código que você escreve com um único objetivo: verificar se o seu código principal (o aplicativo em si) está se comportando como deveria.

Em vez de testar o app manualmente no simulador do Xcode repetidas vezes, você cria roteiros automatizados que rodam em frações de segundo. Eles atuam como uma rede de segurança: se uma refatoração futura quebrar algo que já funcionava, o teste falha imediatamente, barrando o erro antes que ele chegue ao usuário final. Para aplicar essa proteção de forma eficiente e sem sobrecarregar o projeto, dividimos os testes em diferentes categorias, cada uma com seu próprio foco.

image.png


1. Testes Unitários (Unit Testing)

Nos testes unitários, o foco é a regra de negócio. Eles devem ser extremamente rápidos (rodar em milissegundos) e não devem depender de fatores externos (como internet ou o banco de dados real).

Objetivo: Testar a menor parte do código de forma isolada (geralmente uma função ou método).

Framework: Swift Testing (macros @Test, @Suite).

Validações: Uso de #expect para checagens que podem falhar sem parar o teste e #require para desembrulhar opcionais e parar o teste em caso de erro crítico.

Iniciando o teste unitário com Swift Testing

//NomeDoArquivoTests.swift

//Inicie importando testing
import Testing
import Foundation

//Importe o projeto com o atributo @testing para permitir o acesso a tipos internos 
@testable import NomeDoSeuProjeto

Existem alguns macros que são necessários em determinadas situações

//O macro @Suite serve para aplicar a todos os testes dentro deste grupo
@Suite("Testes do NomeDoSeuArquivo")
//Usamos quando o teste for interagir com códigos que exigem a thread principal
@MainActor

Estrutura básica de teste unitário com Swift Testing

//Este macro substitui o test prefixo do XCTest, informando que há um teste
@Test("Verifica se todayRange calcula corretamente o início e o fim do dia")

func todayRange() {

let calendario = Calendar.current

//Chama a função estática diretamente do ViewModel
let (inicioDoDia, fimDoDia) = DashboardViewModel.todayRange(calendar: calendario)

//Verifica se a data 'inicioDoDia' realmente começa à meia-noite (00:00:00)
let componentesInicio = calendario.dateComponents([.hour, .minute, .second], from: inicioDoDia)

#expect(componentesInicio.hour == 0, "A hora inicial deve ser 0 (meia-noite)")

#expect(componentesInicio.minute == 0, "O minuto inicial deve ser 0")

#expect(componentesInicio.second == 0, "O segundo inicial deve ser 0")

}

//Utilizamos #require para validar condições essenciais para a continuidade de um teste. 
let bebidasSalvas = try context.fetch(FetchDescriptor<Drink>())
let bebida = try #require(bebidasSalvas.first, "A bebida deveria ter sido encontrada no banco")