Introducción a las Pruebas de Rendimiento Móvil

El rendimiento de una aplicación móvil impacta directamente en la retención de usuarios y el éxito del negocio. Los estudios (como se discute en Cross-Platform Mobile Testing: Strategies for Multi-Device Success) (como se discute en Appium 2.0: New Architecture and Cloud Integration for Modern Mobile Testing) muestran que el 53% de los usuarios (como se discute en Detox: Grey-Box Testing for React Native Applications) abandonan aplicaciones que tardan más de 3 segundos en cargar, y el rendimiento deficiente es una de las principales razones para desinstalar apps. Las pruebas de rendimiento aseguran que tu aplicación móvil brinde una experiencia fluida y receptiva en diversos dispositivos y condiciones de red.

A diferencia de las aplicaciones de escritorio tradicionales, las apps móviles enfrentan restricciones únicas: vida útil limitada de la batería, conectividad de red variable, especificaciones de hardware diversas y entornos con recursos limitados. Las pruebas de rendimiento efectivas deben abordar estos desafíos mientras garantizan una experiencia de usuario óptima.

Indicadores Clave de Rendimiento (KPIs) para Apps Móviles:

MétricaObjetivoImpacto Crítico
Tiempo de Inicio< 2 segundosRetención de usuarios
Tasa de Fotogramas60 FPSFluidez de UI
Uso de Memoria< 200 MB (típico)Estabilidad de app
Consumo de Batería< 5% por hora (uso activo)Satisfacción del usuario
Eficiencia de RedConsumo mínimo de datosCosto y velocidad

Tiempo de Inicio y Rendimiento de Arranque

El tiempo de inicio es la primera métrica de rendimiento que experimentan los usuarios. Incluye inicio en frío (app no en memoria), inicio tibio (app en segundo plano) e inicio caliente (app en primer plano).

Medición del Tiempo de Inicio

iOS - Xcode Instruments:

// AppDelegate.swift - Seguimiento de métricas de inicio
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    var launchStartTime: CFAbsoluteTime?

    func application(_ application: UIApplication,
                    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        launchStartTime = CFAbsoluteTimeGetCurrent()

        // Realizar inicialización
        setupServices()

        let launchTime = CFAbsoluteTimeGetCurrent() - (launchStartTime ?? 0)
        print("Tiempo de inicio de app: \(launchTime) segundos")

        // Enviar a analytics
        Analytics.track("app_launch_time", properties: ["duration": launchTime])

        return true
    }
}

Android - App Startup Library:

// MainActivity.kt
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Medir tiempo hasta visualización inicial
        reportFullyDrawn()
    }
}

// Clase Application
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        // Inicializar métricas de inicio
        AppStartup.getInstance(this)
            .addOnStartupCompleteListener { startupTime ->
                Log.d("Startup", "Tiempo: $startupTime ms")
                Analytics.logEvent("app_startup", bundleOf("time_ms" to startupTime))
            }
    }
}

Estrategias de Optimización del Tiempo de Inicio

  1. Diferir inicialización no crítica - Cargar solo componentes esenciales durante el arranque
  2. Carga perezosa - Inicializar funcionalidades cuando se acceden por primera vez
  3. Inicialización en segundo plano - Mover tareas pesadas a hilos en segundo plano
  4. Optimización de activos - Comprimir y optimizar recursos de inicio
  5. Reducir dependencias - Minimizar SDKs de terceros cargados al inicio

Monitoreo de Uso de Memoria y Detección de Fugas

Los problemas de memoria causan crashes, degradación del rendimiento y mala experiencia de usuario. Los sistemas operativos móviles terminan agresivamente las apps que consumen memoria excesiva.

Herramientas de Profiling de Memoria

iOS Memory Graph Debugger:

// Monitorear uso de memoria programáticamente
class MemoryMonitor {
    static func getCurrentMemoryUsage() -> UInt64 {
        var info = mach_task_basic_info()
        var count = mach_msg_type_number_t(MemoryLayout<mach_task_basic_info>.size)/4

        let kerr: kern_return_t = withUnsafeMutablePointer(to: &info) {
            $0.withMemoryRebound(to: integer_t.self, capacity: 1) {
                task_info(mach_task_self_,
                         task_flavor_t(MACH_TASK_BASIC_INFO),
                         $0,
                         &count)
            }
        }

        if kerr == KERN_SUCCESS {
            return info.resident_size
        }
        return 0
    }

    static func logMemoryUsage() {
        let usedMemory = Double(getCurrentMemoryUsage()) / 1024 / 1024
        print("Uso de memoria: \(String(format: "%.2f", usedMemory)) MB")
    }
}

Android Memory Profiler:

// MemoryMonitor.kt
class MemoryMonitor(private val context: Context) {

    fun getMemoryInfo(): MemoryInfo {
        val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        val memoryInfo = ActivityManager.MemoryInfo()
        activityManager.getMemoryInfo(memoryInfo)

        val runtime = Runtime.getRuntime()
        val usedMemory = runtime.totalMemory() - runtime.freeMemory()

        return MemoryInfo(
            usedMemoryMB = usedMemory / 1024 / 1024,
            totalMemoryMB = runtime.totalMemory() / 1024 / 1024,
            availableMemoryMB = memoryInfo.availMem / 1024 / 1024
        )
    }

    fun detectMemoryLeaks() {
        LeakCanary.install(context)
    }
}

data class MemoryInfo(
    val usedMemoryMB: Long,
    val totalMemoryMB: Long,
    val availableMemoryMB: Long
)

Patrones Comunes de Fugas de Memoria

Lista de Verificación de Detección de Fugas de Memoria:

  • Recursos sin cerrar - Conexiones de base de datos, manejadores de archivos, sockets de red
  • Referencias estáticas - Activity/Context mantenido por campos estáticos
  • Clases internas anónimas - Referencias implícitas a clases externas
  • Event listeners - Callbacks y observadores no registrados
  • Gestión de bitmaps - Imágenes grandes no recicladas apropiadamente
  • Fugas de WebView - No destruidos apropiadamente cuando terminan

Pruebas de Consumo de Batería

El consumo de batería es una preocupación crítica para los usuarios móviles. Las apps que consumen batería excesiva son desinstaladas rápidamente.

Enfoque de Profiling de Batería

Pruebas de Batería iOS:

# Usando Xcode Energy Log
# 1. Ejecutar app con instrumento Energy Log
# 2. Realizar escenarios típicos de usuario
# 3. Analizar impacto energético por componente

# Monitoreo de batería por línea de comandos
xcrun xctrace record --device <device-id> \
  --template 'Energy Log' \
  --output battery_test.trace \
  --time-limit 10m

Android Battery Historian:

# Capturar estadísticas de batería
adb shell dumpsys batterystats --reset
# Usar app durante duración de prueba
adb shell dumpsys batterystats > batterystats.txt

# Generar reporte de Battery Historian
adb bugreport bugreport.zip
# Cargar a https://bathist.ef.lc/ para análisis

Técnicas de Optimización de Batería

Gestión de Tareas en Segundo Plano iOS:

// BackgroundTaskManager.swift
class BackgroundTaskManager {
    private var backgroundTask: UIBackgroundTaskIdentifier = .invalid

    func beginBackgroundTask() {
        backgroundTask = UIApplication.shared.beginBackgroundTask {
            self.endBackgroundTask()
        }
    }

    func endBackgroundTask() {
        if backgroundTask != .invalid {
            UIApplication.shared.endBackgroundTask(backgroundTask)
            backgroundTask = .invalid
        }
    }

    // Optimizar actualizaciones de ubicación
    func optimizeLocationUpdates() {
        let locationManager = CLLocationManager()
        locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
        locationManager.pausesLocationUpdatesAutomatically = true
        locationManager.allowsBackgroundLocationUpdates = false
    }
}

Optimización de Modo Doze Android:

// PowerOptimization.kt
class PowerOptimization(private val context: Context) {

    fun scheduleBatteryEfficientWork() {
        val constraints = Constraints.Builder()
            .setRequiresBatteryNotLow(true)
            .setRequiresCharging(false)
            .build()

        val workRequest = PeriodicWorkRequestBuilder<DataSyncWorker>(
            15, TimeUnit.MINUTES
        )
            .setConstraints(constraints)
            .build()

        WorkManager.getInstance(context).enqueue(workRequest)
    }

    fun isIgnoringBatteryOptimizations(): Boolean {
        val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
        return powerManager.isIgnoringBatteryOptimizations(context.packageName)
    }
}

Rendimiento y Optimización de Red

La eficiencia de red impacta tanto el rendimiento como la duración de la batería. Las apps móviles deben manejar condiciones de red variables con gracia.

Monitoreo de Red

iOS Network Link Conditioner:

// NetworkMonitor.swift
import Network

class NetworkMonitor {
    private let monitor = NWPathMonitor()
    private let queue = DispatchQueue(label: "NetworkMonitor")

    func startMonitoring() {
        monitor.pathUpdateHandler = { path in
            if path.status == .satisfied {
                print("Red disponible")
                self.logNetworkType(path)
            } else {
                print("Sin conexión de red")
            }
        }
        monitor.start(queue: queue)
    }

    private func logNetworkType(_ path: NWPath) {
        if path.usesInterfaceType(.wifi) {
            print("Usando WiFi")
        } else if path.usesInterfaceType(.cellular) {
            print("Usando Celular")
        }
    }

    // Medir rendimiento de solicitud de red
    func measureRequestTime(url: URL, completion: @escaping (TimeInterval) -> Void) {
        let startTime = CFAbsoluteTimeGetCurrent()

        URLSession.shared.dataTask(with: url) { data, response, error in
            let endTime = CFAbsoluteTimeGetCurrent()
            let requestTime = endTime - startTime
            completion(requestTime)
        }.resume()
    }
}

Android Network Profiler:

// NetworkPerformanceTracker.kt
class NetworkPerformanceTracker {

    fun trackApiCall(url: String, block: () -> Response): Response {
        val startTime = System.currentTimeMillis()
        var responseSize = 0L

        val response = try {
            block().also { response ->
                responseSize = response.body?.contentLength() ?: 0
            }
        } catch (e: Exception) {
            logNetworkError(url, e)
            throw e
        }

        val duration = System.currentTimeMillis() - startTime

        logNetworkMetrics(NetworkMetrics(
            url = url,
            durationMs = duration,
            responseSizeBytes = responseSize,
            statusCode = response.code
        ))

        return response
    }

    private fun logNetworkMetrics(metrics: NetworkMetrics) {
        Log.d("NetworkPerf", """
            URL: ${metrics.url}
            Duración: ${metrics.durationMs}ms
            Tamaño: ${metrics.responseSizeBytes} bytes
            Estado: ${metrics.statusCode}
        """.trimIndent())
    }
}

data class NetworkMetrics(
    val url: String,
    val durationMs: Long,
    val responseSizeBytes: Long,
    val statusCode: Int
)

Estrategias de Optimización de Red

Optimización de Respuesta API:

TécnicaBeneficioImplementación
Compresión de RespuestaReducir transferencia de datosHabilitar gzip/brotli
PaginaciónReducir tamaño de respuestaImplementar carga basada en páginas
Campos SelectivosMinimizar payloadGraphQL o filtrado de campos
CachéReducir solicitudesHeaders de caché HTTP
Uso de CDNEntrega más rápidaCloudFlare, AWS CloudFront
Connection PoolingReutilizar conexionesHTTP/2, conexiones persistentes

Profiling de CPU y GPU

El rendimiento de CPU y GPU afecta directamente la capacidad de respuesta de la app y el consumo de batería.

Profiling con iOS Instruments

# Profiling de CPU
instruments -t "Time Profiler" -D cpu_profile.trace -l 60000 YourApp.app

# Profiling de GPU
instruments -t "Metal System Trace" -D gpu_profile.trace YourApp.app

Optimizando operaciones intensivas en CPU:

// PerformanceOptimization.swift
class ImageProcessor {

    // Operación intensiva en CPU - optimizar con GCD
    func processImagesInParallel(images: [UIImage]) -> [UIImage] {
        let queue = DispatchQueue(label: "imageProcessing",
                                 attributes: .concurrent)
        var processedImages: [UIImage] = []
        let group = DispatchGroup()

        for image in images {
            group.enter()
            queue.async {
                let processed = self.applyFilter(to: image)
                processedImages.append(processed)
                group.leave()
            }
        }

        group.wait()
        return processedImages
    }

    // Usar GPU para procesamiento de imágenes cuando esté disponible
    func applyFilterGPU(to image: UIImage) -> UIImage {
        let context = CIContext(options: [.useSoftwareRenderer: false])
        let filter = CIFilter(name: "CISepiaTone")
        filter?.setValue(CIImage(image: image), forKey: kCIInputImageKey)

        if let output = filter?.outputImage,
           let cgImage = context.createCGImage(output, from: output.extent) {
            return UIImage(cgImage: cgImage)
        }
        return image
    }
}

Profiler de CPU/GPU Android

// PerformanceTracker.kt
class PerformanceTracker {

    fun trackMethodPerformance(methodName: String, block: () -> Unit) {
        val startTime = System.nanoTime()
        val startCpu = Debug.threadCpuTimeNanos()

        block()

        val cpuTime = (Debug.threadCpuTimeNanos() - startCpu) / 1_000_000
        val wallTime = (System.nanoTime() - startTime) / 1_000_000

        Log.d("Performance", """
            Método: $methodName
            Tiempo CPU: ${cpuTime}ms
            Tiempo Total: ${wallTime}ms
        """.trimIndent())
    }

    // Detectar sobredibujo de GPU
    fun checkGpuOverdraw(activity: Activity) {
        val debugOverdraw = Settings.Global.getString(
            activity.contentResolver,
            "debug.hwui.overdraw"
        )
        Log.d("GPU", "Configuración de overdraw: $debugOverdraw")
    }
}

Tasa de Fotogramas y Capacidad de Respuesta de UI

Una UI fluida requiere mantener 60 FPS (fotogramas por segundo). Los fotogramas perdidos crean interfaces entrecortadas y no responsivas.

Monitoreo de Tasa de Fotogramas

Contador FPS iOS:

// FPSMonitor.swift
class FPSMonitor {
    private var displayLink: CADisplayLink?
    private var lastTimestamp: CFTimeInterval = 0
    private var frameCount: Int = 0

    func startMonitoring() {
        displayLink = CADisplayLink(target: self,
                                   selector: #selector(displayLinkTick))
        displayLink?.add(to: .main, forMode: .common)
    }

    @objc private func displayLinkTick(displayLink: CADisplayLink) {
        if lastTimestamp == 0 {
            lastTimestamp = displayLink.timestamp
            return
        }

        frameCount += 1
        let elapsed = displayLink.timestamp - lastTimestamp

        if elapsed >= 1.0 {
            let fps = Double(frameCount) / elapsed
            print("FPS: \(Int(fps))")

            frameCount = 0
            lastTimestamp = displayLink.timestamp

            // Alertar si FPS cae por debajo del umbral
            if fps < 55 {
                print("⚠️ FPS bajo detectado: \(Int(fps))")
            }
        }
    }

    func stopMonitoring() {
        displayLink?.invalidate()
        displayLink = nil
    }
}

Métricas de Fotogramas Android:

// FrameMetricsTracker.kt
class FrameMetricsTracker(private val activity: Activity) {

    fun startTracking() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            activity.window.addOnFrameMetricsAvailableListener(
                { _, frameMetrics, _ ->
                    val totalDuration = frameMetrics.getMetric(
                        FrameMetrics.TOTAL_DURATION
                    )
                    val totalDurationMs = totalDuration / 1_000_000.0

                    // Tiempo de fotograma debe ser < 16.67ms para 60 FPS
                    if (totalDurationMs > 16.67) {
                        Log.w("FrameMetrics", "Fotograma lento: ${totalDurationMs}ms")

                        // Desglosar el fotograma lento
                        logFrameBreakdown(frameMetrics)
                    }
                },
                Handler(Looper.getMainLooper())
            )
        }
    }

    private fun logFrameBreakdown(metrics: FrameMetrics) {
        val inputMs = metrics.getMetric(FrameMetrics.INPUT_HANDLING_DURATION) / 1_000_000.0
        val animationMs = metrics.getMetric(FrameMetrics.ANIMATION_DURATION) / 1_000_000.0
        val layoutMs = metrics.getMetric(FrameMetrics.LAYOUT_MEASURE_DURATION) / 1_000_000.0
        val drawMs = metrics.getMetric(FrameMetrics.DRAW_DURATION) / 1_000_000.0

        Log.d("FrameBreakdown", """
            Entrada: ${inputMs}ms
            Animación: ${animationMs}ms
            Layout: ${layoutMs}ms
            Dibujo: ${drawMs}ms
        """.trimIndent())
    }
}

Optimización de Rendimiento de UI

Cuellos de botella comunes de rendimiento:

  • Jerarquías de vista complejas - Aplanar layouts, usar ConstraintLayout (Android)
  • Sobredibujo - Reducir vistas superpuestas, eliminar fondos innecesarios
  • Bloqueo del hilo principal - Mover operaciones pesadas a hilos en segundo plano
  • Renderizado de listas ineficiente - Usar RecyclerView (Android), UICollectionView (iOS) con reutilización de celdas
  • Imágenes grandes - Redimensionar y cachear apropiadamente para tamaño de visualización
  • Operaciones síncronas - Hacer llamadas de red/base de datos asíncronas

Optimización del Tamaño de la App

Un tamaño de app menor mejora las tasas de descarga y reduce preocupaciones de almacenamiento.

Herramientas de Análisis de Tamaño

iOS App Thinning:

# Generar reporte de tamaño de app
xcodebuild -exportArchive \
  -archivePath YourApp.xcarchive \
  -exportPath AppSizeReport \
  -exportOptionsPlist exportOptions.plist \
  -exportFormat APP_SIZE_REPORT

# Analizar tamaño del binario
xcrun dyldinfo -size YourApp.app/YourApp

Android APK Analyzer:

# Construir APK con análisis de tamaño
./gradlew assembleRelease

# Analizar tamaño de APK
bundletool dump manifest --bundle=app-release.aab
bundletool get-size total --bundle=app-release.aab

# Generar reporte de tamaño
./gradlew app:analyzeReleaseBundle

Estrategias de Reducción de Tamaño:

TécnicaAhorro iOSAhorro Android
Compresión de imágenes20-40%20-40%
Ofuscación/minificación de código10-15%15-25% (ProGuard)
Eliminar recursos no usados5-10%10-20% (shrinkResources)
Módulos de características dinámicasN/A30-50% (bajo demanda)
App thinning/bundles20-30%30-40% (AAB)

Pruebas Automatizadas de Rendimiento

La automatización asegura monitoreo consistente de rendimiento entre releases.

Pruebas de Rendimiento XCTest iOS

// PerformanceTests.swift
import XCTest

class PerformanceTests: XCTestCase {

    func testAppLaunchPerformance() {
        measure(metrics: [XCTApplicationLaunchMetric()]) {
            XCUIApplication().launch()
        }
    }

    func testScrollingPerformance() {
        let app = XCUIApplication()
        app.launch()

        measure(metrics: [XCTOSSignpostMetric.scrollDecelerationMetric]) {
            app.tables.firstMatch.swipeUp(velocity: .fast)
        }
    }

    func testMemoryPerformance() {
        measure(metrics: [XCTMemoryMetric()]) {
            // Realizar operación intensiva en memoria
            let viewController = DataViewController()
            viewController.loadLargeDataset()
        }
    }

    func testNetworkPerformance() {
        let expectation = XCTestExpectation(description: "Llamada API")

        measure {
            NetworkService.shared.fetchData { result in
                expectation.fulfill()
            }
            wait(for: [expectation], timeout: 10.0)
        }
    }
}

Android Macrobenchmark

// PerformanceBenchmark.kt
@RunWith(AndroidJUnit4::class)
class PerformanceBenchmark {

    @get:Rule
    val benchmarkRule = MacrobenchmarkRule()

    @Test
    fun startupCompilation() = benchmarkRule.measureRepeated(
        packageName = "com.yourapp",
        metrics = listOf(StartupTimingMetric()),
        iterations = 10,
        startupMode = StartupMode.COLD
    ) {
        pressHome()
        startActivityAndWait()
    }

    @Test
    fun scrollPerformance() = benchmarkRule.measureRepeated(
        packageName = "com.yourapp",
        metrics = listOf(FrameTimingMetric()),
        iterations = 5
    ) {
        startActivityAndWait()

        val recyclerView = device.findObject(By.res("article_list"))
        repeat(10) {
            recyclerView.scroll(Direction.DOWN, 0.8f)
            device.waitForIdle()
        }
    }
}

Benchmarking de Rendimiento y Métricas

Establecer líneas base de rendimiento y rastrear métricas a lo largo del tiempo.

Panel de Métricas Clave de Rendimiento

// PerformanceMetrics.kt
data class AppPerformanceMetrics(
    val launchTimeMs: Long,
    val memoryUsageMB: Double,
    val batteryDrainPercent: Double,
    val avgFrameTimeMs: Double,
    val networkLatencyMs: Long,
    val appSizeMB: Double,
    val crashFreeRate: Double
)

class PerformanceBenchmark {

    fun generatePerformanceReport(metrics: AppPerformanceMetrics): BenchmarkReport {
        val benchmarks = loadHistoricalBenchmarks()

        return BenchmarkReport(
            metrics = metrics,
            launchTimeDelta = calculateDelta(metrics.launchTimeMs, benchmarks.avgLaunchTime),
            memoryDelta = calculateDelta(metrics.memoryUsageMB, benchmarks.avgMemory),
            passed = metrics.launchTimeMs < 2000 &&
                     metrics.memoryUsageMB < 200 &&
                     metrics.avgFrameTimeMs < 16.67
        )
    }

    private fun calculateDelta(current: Double, baseline: Double): Double {
        return ((current - baseline) / baseline) * 100
    }
}

Tabla de Comparación de Rendimiento:

Nivel de DispositivoTiempo InicioMemoriaTasa FotogramasBatería/Hora
Gama alta (2023+)< 1.5s< 150 MB60 FPS< 3%
Gama media (2021-22)< 2.5s< 200 MB60 FPS< 5%
Gama baja (2019-20)< 4s< 250 MB30-60 FPS< 7%

Monitoreo de Rendimiento en CI/CD

Integrar pruebas de rendimiento en pipelines de integración continua.

Pruebas de Rendimiento con GitHub Actions

# .github/workflows/performance-test.yml
name: Pruebas de Rendimiento

on:
  pull_request:
    branches: [ main, develop ]
  schedule:
    - cron: '0 0 * * 0'  # Semanalmente

jobs:
  ios-performance:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v3

      - name: Ejecutar Pruebas de Rendimiento iOS
        run: |
          xcodebuild test \
            -scheme YourApp \
            -destination 'platform=iOS Simulator,name=iPhone 14' \
            -testPlan PerformanceTests \
            -resultBundlePath TestResults.xcresult

      - name: Analizar Resultados
        run: |
          xcrun xcresulttool get --format json \
            --path TestResults.xcresult > results.json

          python scripts/analyze_performance.py results.json

      - name: Comentar PR con Resultados
        uses: actions/github-script@v6
        with:
          script: |
            const fs = require('fs');
            const report = fs.readFileSync('performance_report.md', 'utf8');
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: report
            });

  android-performance:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Configurar JDK
        uses: actions/setup-java@v3
        with:
          distribution: 'temurin'
          java-version: '17'

      - name: Ejecutar Pruebas de Rendimiento Android
        run: |
          ./gradlew connectedAndroidTest \
            -Pandroid.testInstrumentationRunnerArguments.class=\
            com.yourapp.PerformanceBenchmark

      - name: Cargar Resultados de Benchmark
        run: |
          ./gradlew uploadBenchmarkResults \
            --benchmarkData build/outputs/benchmark/results.json

Detección de Regresiones de Rendimiento

# scripts/detect_performance_regression.py
import json
import sys

def detect_regression(current_metrics, baseline_metrics, threshold=10):
    regressions = []

    metrics_to_check = [
        'launch_time_ms',
        'memory_usage_mb',
        'avg_frame_time_ms'
    ]

    for metric in metrics_to_check:
        current = current_metrics.get(metric, 0)
        baseline = baseline_metrics.get(metric, 0)

        if baseline > 0:
            change_percent = ((current - baseline) / baseline) * 100

            if change_percent > threshold:
                regressions.append({
                    'metric': metric,
                    'current': current,
                    'baseline': baseline,
                    'change_percent': change_percent
                })

    return regressions

def main():
    with open('current_results.json') as f:
        current = json.load(f)

    with open('baseline_results.json') as f:
        baseline = json.load(f)

    regressions = detect_regression(current, baseline)

    if regressions:
        print("⚠️ Regresiones de rendimiento detectadas:")
        for reg in regressions:
            print(f"  {reg['metric']}: {reg['change_percent']:.1f}% incremento")
        sys.exit(1)
    else:
        print("✅ No se detectaron regresiones de rendimiento")
        sys.exit(0)

if __name__ == '__main__':
    main()

Conclusión

Las pruebas de rendimiento de aplicaciones móviles son un proceso continuo que requiere monitorear múltiples métricas en diversos dispositivos y condiciones. Al implementar estrategias comprensivas de pruebas de rendimiento—incluyendo optimización del tiempo de inicio, gestión de memoria, eficiencia de batería, optimización de red y monitoreo automatizado—aseguras que tu app brinde una experiencia de usuario excepcional.

Conclusiones Clave:

  • Comienza temprano - Integra pruebas de rendimiento desde el inicio del desarrollo
  • Automatiza las pruebas - Usa pipelines CI/CD para monitoreo continuo de rendimiento
  • Prueba en dispositivos reales - Los simuladores no representan con precisión el rendimiento del mundo real
  • Monitorea producción - Usa herramientas APM (Firebase, New Relic, DataDog) para monitoreo en vivo
  • Establece líneas base - Establece benchmarks de rendimiento y rastrea tendencias a lo largo del tiempo
  • Prioriza la experiencia del usuario - Enfócate en métricas que impactan directamente a los usuarios

Las pruebas de rendimiento no son solo sobre cumplir benchmarks técnicos—se trata de entregar aplicaciones rápidas, responsivas y eficientes en batería que los usuarios aman. Invierte en infraestructura de pruebas de rendimiento temprano, monitorea continuamente e itera basándote en datos del mundo real para construir aplicaciones móviles de clase mundial.