✨ 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>
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.