cat swift/testing-swift-testing-xctest.md

Lezione 42 55 min

Testing con Swift Testing e XCTest

Swift Testing e XCTest possono convivere: imparare test espressivi, parametrici e asincroni rende il package affidabile.

Swift ha una lunga storia con XCTest è un framework moderno chiamato Swift Testing. Nel codice attuale puoi incontrare entrambi.

Swift Testing offre API espressive, macro come #expect, test parametrizzati è un modello adatto al Swift moderno. XCTest resta ampiamente usato e supportato.

La cosa importante non è scegliere una bandiera: è scrivere test che proteggano comportamento reale.

Test con Swift Testing

import Testing
@testable import CouponCore

@Test
func percentualeRiduceTotale() throws {
  let totale = Prezzo(10_000)!
  let sconto = Sconto.percentuale(10)

  let finale = try CouponEngine().totaleApplicando(sconto, a: totale)

  #expect(finale == Prezzo(9_000))
}

#expect produce messaggi di fallimento più informativi rispetto a molti assert tradizionali.

Test parametrizzati

@Test(arguments: [
  (10_000, 10, 9_000),
  (5_000, 20, 4_000),
  (1_000, 100, 0)
])
func scontoPercentuale(input: Int, percentuale: Int, expected: Int) throws {
  let totale = Prezzo(input)!
  let sconto = Sconto.percentuale(percentuale)

  let finale = try CouponEngine().totaleApplicando(sconto, a: totale)

  #expect(finale == Prezzo(expected))
}

I test parametrizzati riducono duplicazione senza perdere casi.

Test asincroni

@Test
func repositoryRestituisceCoupon() async throws {
  let repository = CouponRepositoryInMemoria(coupon: [.welcome10])

  let coupon = try await repository.couponDisponibili()

  #expect(coupon.count == 1)
}

Il test può essere async throws, come il codice reale.

XCTest

Con XCTest:

import XCTest
@testable import CouponCore

final class CouponEngineTests: XCTestCase {
  func testPercentualeRiduceTotale() throws {
    let totale = Prezzo(10_000)!
    let sconto = Sconto.percentuale(10)

    let finale = try CouponEngine().totaleApplicando(sconto, a: totale)

    XCTAssertEqual(finale, Prezzo(9_000))
  }
}

XCTest è ancora molto diffuso. Saperlo leggere è importante.

Migrazione graduale

Puoi far convivere test XCTest è Swift Testing nello stesso package. Questo permette migrazioni graduali.

Non riscrivere tutti i test solo per moda. Aggiungi Swift Testing dove migliora chiarezza, parametrizzazione o diagnostica.

Testare errori

Swift Testing:

@Test
func couponSottoMinimoFallisce() async {
  #expect(throws: ErroreCoupon.carrelloSottoMinimo(richiesto: 5_000, attuale: 2_000)) {
    try CouponEngine().valida(coupon, nel: contesto)
  }
}

Se il tuo errore è Equatable, puoi verificare il caso preciso.

Test e dominio

Testa comportamento, non implementazione privata.

Buono:

  • un coupon valido si applica;
  • un coupon sotto soglia fallisce;
  • il totale non diventa negativo;
  • una categoria mancante produce errore corretto.

Meno utile:

  • verificare il nome di una variabile interna;
  • testare helper privati direttamente;
  • duplicare l’implementazione nel test.

Fixtures

Evita setup lunghi in ogni test:

extension Prodotto {
  static let libro = Prodotto(...)
}

extension Coupon {
  static let welcome10 = Coupon(...)
}

Le fixture devono essere leggibili e vicine al dominio.

Checklist

Per test professionali:

  • test piccoli;
  • nomi che descrivono comportamento;
  • dati di test leggibili;
  • errori verificati precisamente;
  • test async dove serve;
  • parametrizzazione per casi simili;
  • nessuna dipendenza da rete reale nei test unitari;
  • XCTest è Swift Testing possono convivere.

Esercizio

Scrivi test per CouponEngine:

  • sconto percentuale;
  • sconto fisso;
  • totale mai negativo;
  • minimo carrello non raggiunto;
  • categoria mancante;
  • tre casi parametrizzati per percentuali diverse.

Usa Swift Testing. Poi scrivi un test equivalente in XCTest per confrontare lo stile.