Introdução: por que os testes são a base de um aplicativo Swift confiável
\n\nO desenvolvimento para iOS e macOS exige alta confiabilidade do código. Um erro na lógica de funcionamento do aplicativo pode custar a experiência do usuário e, em projetos comerciais, dinheiro. Testar em Swift não é apenas uma "boa prática", mas uma etapa obrigatória do desenvolvimento. Permite identificar bugs nos estágios iniciais, simplificar a refatoração e aumentar a qualidade do código.
\n\nNeste artigo, abordaremos três níveis de teste: unit (unitários), integration (integração) e e2e (ponta a ponta). Você aprenderá como eles diferem, quando aplicar cada um e verá exemplos práticos em Swift usando o framework XCTest.
1. Testes unitários em Swift: o básico
\n\nTestes unitários verificam módulos individuais (funções, métodos, classes) de forma isolada. Seu principal objetivo é garantir que cada "unidade" de código funcione corretamente.
\n\nConfiguração do XCTest
\n\nO XCTest é integrado ao Xcode. Para criar um target de teste:
\n- \n
- Abra o projeto no Xcode. \n
- Selecione File → New → Target. \n
- Escolha Unit Testing Bundle. \n
Após a criação, você obterá um arquivo como YourAppTests.swift.
Exemplo de teste unitário
\n\nSuponha que temos uma função de cálculo de desconto:
\n\nfunc calculateDiscount(price: Double, discount: Double) -> Double {\n guard discount >= 0 && discount <= 100 else { return price }\n return price * (1 - discount / 100)\n}\n\n\nEscrevemos o teste:
\n\nimport XCTest\n@testable import YourApp\n\nclass DiscountTests: XCTestCase {\n func testCalculateDiscount_ValidInput() {\n let result = calculateDiscount(price: 1000, discount: 20)\n XCTAssertEqual(result, 800, accuracy: 0.01)\n }\n \n func testCalculateDiscount_InvalidDiscount() {\n let result = calculateDiscount(price: 500, discount: 150)\n XCTAssertEqual(result, 500)\n }\n}\n\n\nBest practices para testes unitários:
\n- \n
- Um teste — uma verificação. \n
- Use
setUp()para inicializar objetos comuns. \n - Evite dependências de rede, banco de dados ou UI — use mocks. \n
2. Testes de integração: verificando a interação entre componentes
\n\nTestes de integração verificam como diferentes módulos trabalham juntos. Por exemplo, a interação do modelo de dados com a camada de rede ou o banco de dados CoreData.
\n\nExemplo: testando uma requisição de rede
\n\nCriamos um serviço simples:
\n\nclass NetworkService {\n func fetchUser(completion: @escaping (Result<User, Error>) -> Void) {\n // Requisição real à API\n }\n}\n\n\nUm teste de integração pode verificar se o serviço processa corretamente a resposta de um servidor real (ou de um servidor mock local):
\n\nclass NetworkServiceIntegrationTests: XCTestCase {\n var service: NetworkService!\n \n override func setUp() {\n super.setUp()\n service = NetworkService()\n }\n \n func testFetchUser_ReturnsUser() {\n let expectation = self.expectation(description: "Fetch user")\n \n service.fetchUser { result in\n switch result {\n case .success(let user):\n XCTAssertNotNil(user.name)\n case .failure:\n XCTFail("Request failed")\n }\n expectation.fulfill()\n }\n \n waitForExpectations(timeout: 5)\n }\n}\n\n\nImportante: Testes de integração são mais lentos que testes unitários, por isso são executados com menos frequência (por exemplo, antes de um merge no main).
\n\n3. Testes E2E (ponta a ponta) com XCUITest
\n\nTestes E2E (End-to-End) simulam ações do usuário: clicar em botões, inserir texto, verificar a exibição da UI. Em Swift, para isso