✨ Features: - App iOS completa para leer manga sin publicidad - Scraper con WKWebView para manhwaweb.com - Sistema de descargas offline - Lector con zoom y navegación - Favoritos y progreso de lectura - Compatible con iOS 15+ y Sideloadly/3uTools 📦 Contenido: - Backend Node.js con Puppeteer (opcional) - App iOS con SwiftUI - Scraper de capítulos e imágenes - Sistema de almacenamiento local - Testing completo - Documentación exhaustiva 🧪 Prueba: Capítulo 789 de One Piece descargado exitosamente - 21 páginas descargadas - 4.68 MB total - URLs verificadas y funcionales 🎉 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
12 KiB
MangaReader Test Suite
Suite completa de tests para el proyecto MangaReader usando XCTest.
Tabla de Contenidos
Descripción General
Esta suite de tests cubre todos los componentes principales del proyecto MangaReader:
- Modelos de Datos - Validación de Codable, edge cases, y lógica de negocio
- StorageService - Almacenamiento local, favoritos, progreso de lectura
- ManhwaWebScraper - Web scraping y parsing de HTML/JavaScript
- Integración - Flujos completos que conectan múltiples componentes
Estructura de Tests
Tests/
├── ModelTests.swift # Tests para modelos de datos
├── StorageServiceTests.swift # Tests para servicio de almacenamiento
├── ManhwaWebScraperTests.swift # Tests para web scraper
├── IntegrationTests.swift # Tests de integración
├── TestHelpers.swift # Helpers y factories para tests
└── XCTestSuiteExtensions.swift # Extensiones de XCTest
Ejecutar Tests
Desde Xcode
- Abrir el proyecto en Xcode
- Cmd + U para ejecutar todos los tests
- Cmd + 6 para abrir el Test Navigator
- Click derecho en un test específico para ejecutarlo
Desde Línea de Comandos
# Ejecutar todos los tests
xcodebuild test -scheme MangaReader -destination 'platform=iOS Simulator,name=iPhone 15'
# Ejecutar tests específicos
xcodebuild test -scheme MangaReader -only-testing:MangaReaderTests/ModelTests
# Ejecutar con cobertura
xcodebuild test -scheme MangaReader -enableCodeCoverage YES
Guía de Tests
ModelTests.swift
Prueba todos los modelos de datos del proyecto.
Tests Incluidos:
Manga Model:
testMangaInitialization- Verifica inicialización correctatestMangaCodableSerialization- Prueba encoding/decoding JSONtestMangaDisplayStatus- Verifica traducción de estadostestMangaHashable- Prueba conformidad con Hashable
Chapter Model:
testChapterInitialization- Inicialización con valores por defectotestChapterDisplayNumber- Formato de número de capítulotestChapterProgress- Cálculo de progreso de lecturatestChapterCodableSerialization- Serialización JSON
MangaPage Model:
testMangaPageInitialization- Creación de páginastestMangaPageThumbnailURL- URLs de thumbnailstestMangaPageCodableSerialization- Serialización
ReadingProgress Model:
testReadingProgressInitialization- Creación de progresotestReadingProgressIsCompleted- Lógica de completacióntestReadingProgressCodableSerialization- Persistencia
DownloadedChapter Model:
testDownloadedChapterInitialization- Creación de capítulos descargadostestDownloadedChapterDisplayTitle- Formato de títulostestDownloadedChapterCodableSerialization- Serialización completa
Edge Cases:
testMangaWithEmptyGenres- Manejo de arrays vacíostestMangaWithNilCoverImage- Imagen de portada opcionaltestChapterWithZeroNumber- Capítulo cerotestMangaPageWithNegativeIndex- Índices negativos
StorageServiceTests.swift
Prueba el servicio de almacenamiento local.
Tests Incluidos:
Favorites:
testSaveFavorite- Guardar un favoritotestSaveMultipleFavorites- Guardar varios favoritostestSaveDuplicateFavorite- Evitar duplicadostestRemoveFavorite- Eliminar favoritotestIsFavorite- Verificar si es favorito
Reading Progress:
testSaveReadingProgress- Guardar progresotestSaveMultipleReadingProgress- Múltiples progresostestUpdateExistingReadingProgress- Actualizar progresotestGetLastReadChapter- Obtener último capítulo leídotestGetReadingProgressWhenNotExists- Progreso inexistente
Downloaded Chapters:
testSaveDownloadedChapter- Guardar metadatos de capítulotestIsChapterDownloaded- Verificar descargatestGetDownloadedChapters- Listar capítulostestDeleteDownloadedChapter- Eliminar capítulo
Image Caching:
testSaveAndLoadImage- Guardar y cargar imagentestLoadNonExistentImage- Imagen inexistentetestGetImageURL- Obtener URL de imagen
Storage Management:
testGetStorageSize- Calcular tamaño usadotestClearAllDownloads- Limpiar todo el almacenamientotestFormatFileSize- Formatear tamaño a legible
Concurrent Operations:
testConcurrentImageSave- Guardar imágenes concurrentemente
ManhwaWebScraperTests.swift
Prueba el web scraper con mocks de WKWebView.
Tests Incluidos:
Error Handling:
testScrapingErrorDescriptions- Descripciones de errorestestScrapingErrorLocalizedError- Conformidad con LocalizedError
Chapter Parsing:
testChapterParsingFromJavaScriptResult- Parsear respuesta JStestChapterParsingWithInvalidData- Manejar datos inválidostestChapterDeduplication- Eliminar capítulos duplicadostestChapterSorting- Ordenar capítulos
Image Parsing:
testImageParsingFromJavaScriptResult- Parsear URLs de imágenestestImageParsingWithEmptyArray- Array vacío de imágenestestImageParsingWithInvalidURLs- Filtrar URLs inválidas
Manga Info Parsing:
testMangaInfoParsingFromJavaScriptResult- Extraer info de mangatestMangaInfoParsingWithEmptyFields- Campos vacíostestMangaStatusParsing- Normalizar estados
URL Construction:
testMangaURLConstruction- Construir URLs de mangatestChapterURLConstruction- Construir URLs de capítulotestURLConstructionWithSpecialCharacters- Caracteres especiales
Edge Cases:
testChapterNumberExtraction- Extraer números de capítulotestChapterSlugExtraction- Extraer slugstestDuplicateRemovalPreservingOrder- Eliminar duplicados manteniendo orden
IntegrationTests.swift
Prueba flujos completos que integran múltiples componentes.
Tests Incluidos:
Complete Flow:
testCompleteScrapingAndStorageFlow- Scraper -> StoragetestChapterDownloadFlow- Descarga completa de capítulotestReadingProgressTrackingFlow- Seguimiento de lectura
Multi-Manga Scenarios:
testMultipleMangasProgressTracking- Varios mangastestMultipleChapterDownloads- Descargas de múltiples capítulos
Error Handling:
testDownloadFlowWithMissingImages- Imágenes faltantestestStorageCleanupFlow- Limpieza de almacenamiento
Data Persistence:
testDataPersistenceAcrossOperations- Persistencia de datos
Concurrent Operations:
testConcurrentFavoriteOperations- Operaciones concurrentes favoritostestConcurrentProgressOperations- Operaciones concurrentes progresotestConcurrentImageOperations- Guardado concurrente de imágenes
Large Scale:
testLargeScaleFavoriteOperations- 1000 favoritostestLargeScaleProgressOperations- 500 progresos
TestHelpers.swift
Proporciona helpers y factories para crear datos de prueba:
TestDataFactory
Crea objetos de prueba:
let manga = TestDataFactory.createManga(
slug: "test-manga",
title: "Test Manga"
)
let chapter = TestDataFactory.createChapter(number: 1)
let chapters = TestDataFactory.createChapters(count: 10)
ImageTestHelpers
Crea imágenes de prueba:
let image = ImageTestHelpers.createTestImage(
color: .blue,
size: CGSize(width: 800, height: 1200)
)
FileSystemTestHelpers
Operaciones de sistema de archivos:
let tempDir = try FileSystemTestHelpers.createTemporaryDirectory()
try FileSystemTestHelpers.createTestChapterStructure(
mangaSlug: "test",
chapterNumber: 1,
pageCount: 10,
in: tempDir
)
StorageTestHelpers
Limpieza y preparación de almacenamiento:
StorageTestHelpers.clearAllStorage()
StorageTestHelpers.seedTestData(
favoriteCount: 5,
progressCount: 10
)
Mejores Prácticas
1. Independencia de Tests
Cada test debe ser independiente y poder ejecutarse solo:
override func setUp() {
super.setUp()
// Limpiar estado antes del test
UserDefaults.standard.removeObject(forKey: "favoritesKey")
}
override func tearDown() {
// Limpiar estado después del test
super.tearDown()
}
2. Nombres Descriptivos
Usa nombres que describan qué se está probando:
// ✅ Bueno
func testSaveDuplicateFavoriteDoesNotAddDuplicate()
// ❌ Malo
func testFavorite()
3. Un Assert por Test
Cuando sea posible, usa un assert por test:
// ✅ Bueno
func testFavoriteIsSaved() {
storageService.saveFavorite(mangaSlug: "test")
XCTAssertTrue(storageService.isFavorite(mangaSlug: "test"))
}
func testFavoriteIsRemoved() {
storageService.saveFavorite(mangaSlug: "test")
storageService.removeFavorite(mangaSlug: "test")
XCTAssertFalse(storageService.isFavorite(mangaSlug: "test"))
}
// ❌ Evitar
func testFavoriteOperations() {
storageService.saveFavorite(mangaSlug: "test")
XCTAssertTrue(storageService.isFavorite(mangaSlug: "test"))
storageService.removeFavorite(mangaSlug: "test")
XCTAssertFalse(storageService.isFavorite(mangaSlug: "test"))
}
4. AAA Pattern
Usa el patrón Arrange-Act-Assert:
func testChapterProgressCalculation() {
// Arrange - Preparar el test
var chapter = Chapter(number: 1, title: "Test", url: "", slug: "")
let expectedPage = 5
// Act - Ejecutar la acción
chapter.lastReadPage = expectedPage
// Assert - Verificar el resultado
XCTAssertEqual(chapter.progress, Double(expectedPage))
}
5. Mock de Dependencias
No hagas llamadas de red reales en tests unitarios:
// ✅ Bueno - Mock
let mockJSResult = [["number": 10, "title": "Chapter 10"]]
let chapters = parseChaptersFromJS(mockJSResult)
// ❌ Malo - Llamada real
let chapters = await scraper.scrapeChapters(mangaSlug: "test")
6. Tests Asíncronos
Usa async/await apropiadamente:
func testAsyncImageSave() async throws {
let image = createTestImage()
let url = try await storageService.saveImage(
image,
mangaSlug: "test",
chapterNumber: 1,
pageIndex: 0
)
XCTAssertTrue(FileManager.default.fileExists(atPath: url.path))
}
Cobertura de Código
Objetivos de cobertura:
- Modelos: 95%+ (lógica crítica de datos)
- StorageService: 90%+ (manejo de archivos y persistencia)
- Scraper: 85%+ (con mocks de WKWebView)
- Integración: 80%+ (flujos críticos de usuario)
Troubleshooting
Tests Fallan Intermittentemente
Si un test falla solo algunas veces:
- Verifica que hay cleanup adecuado en
tearDown() - Asegura que los tests son independientes
- Usa
waitForapropiadamente para operaciones asíncronas
Tests de Performance Fallan
Si los tests de rendimiento fallan en diferentes máquinas:
- Ajusta las métricas según el hardware
- Usa medidas relativas en lugar de absolutas
- Considera deshabilitar tests de performance en CI
Memory Leaks en Tests
Para detectar memory leaks:
func testNoMemoryLeak() {
let instance = MyClass()
assertNoMemoryLeak(instance)
}
Recursos Adicionales
Contribuir
Para agregar nuevos tests:
- Decide si es unit test, integration test, o performance test
- Agrega el test al archivo apropiado
- Usa los helpers en
TestHelpers.swiftcuando sea posible - Asegura que el test es independiente
- Agrega documentación si el test es complejo
- Ejecuta todos los tests para asegurar que nada se rompe
Licencia
Mismo que el proyecto principal.