Marco de diseño de aplicaciones y mejores prácticas basadas en la automatización

Un concepto que promueve cómo los desarrolladores pueden diseñar sus aplicaciones para que sean compatibles con la automatización desde el primer día: la perspectiva de un tester.

Introducción

Como probador de automatización, he dedicado incontables horas a lidiar con aplicaciones que parecían diseñadas deliberadamente para resistir la automatización. Selectores frágiles que fallan con pequeños ajustes en la interfaz de usuario, componentes sin propiedades identificables y flujos de trabajo complejos ocultos tras estados poco claros: estas son las frustraciones diarias a las que nos enfrentamos.

Pero aquí está la clave: la mayoría de estos problemas son prevenibles. Cuando los desarrolladores diseñan aplicaciones teniendo en cuenta la automatización desde el principio, las pruebas se vuelven más rápidas, más fiables y mucho más fáciles de mantener.

No se trata solo de facilitarnos el trabajo, sino de ofrecer software de mayor calidad con mayor rapidez.

Este artículo comparte recomendaciones prácticas que todo desarrollador de aplicaciones debería seguir para que sus aplicaciones estén preparadas para la automatización.

Hacer que los elementos sean identificables y estables.

El problema:

Muchas aplicaciones dependen exclusivamente de nombres de clases CSS o identificadores generados que cambian con cada compilación. Los marcos de automatización como Selenium, Playwright y Appium dependen de localizadores (identificadores únicos) para interactuar con los elementos.

Qué deben hacer los desarrolladores:

  • Agregar la extensión de data-testid atributos a elementos interactivos (botones, campos de entrada, formularios, enlaces)
  • Mantén estos identificadores estables entre versiones; trátalos como API públicas.
  • Evite depender únicamente de nombres de clases dinámicos o posiciones XPath.

Ejemplo real:

// ❌ BAD - Changes frequently 

<button className="btn-primary-v2 btn-submit-form"> 

Submit 

</button> 

// ✅ GOOD - Stable identifier for testers 

<button data-testid="form-submit-button" className="btn-primary-v2"> 

Submit 

</button>

Enviar Formulario

Por qué esto importa:

Cuando agregas data-testidLos evaluadores pueden localizar elementos de forma fiable utilizando page.locator('[data-testid="form-submit-button"]') en Dramaturgo o find_element(By.CSS_SELECTOR, '[data-testid="form-submit-button"]') en Selenio.

Utilice HTML semántico y asociaciones de etiquetas adecuadas.

El problema:

Generic <div> además <span> Además, los elementos sin etiquetas hacen que sea casi imposible para los evaluadores comprender la finalidad de los controles y para que las herramientas de accesibilidad funcionen correctamente.

Qué deben hacer los desarrolladores:

  • Utilice elementos HTML semánticos: <button>, <input>, <label>, <form>
  • Asocie etiquetas con entradas usando <label for="inputId">
  • Usa aria-label or aria-labelledby para componentes complejos

Ejemplo real:

// ❌ BAD - No semantic meaning 

<div onClick={() => setSelected(!selected)}> 

<span>Enable notifications</span> 

</div> 

 

// ✅ GOOD - Semantic and testable 

<label htmlFor="notifications-toggle"> 

<input  

id="notifications-toggle"  

type="checkbox"  

data-testid="notifications-toggle" 

/> 

Enable notifications 

</label>

Perspectiva del evaluador:

Con HTML semántico, puedo usar selectores basados ​​en roles: page.locator('input[role=”checkbox”]') o incluso getByRole(“checkbox”, { name: /enable notifications/i }) en Playwright, lo que hace que las pruebas sean más legibles y resistentes a los cambios de estilo.

Implementar estados de espera explícitos e indicadores de carga.

El problema:

Las aplicaciones con mucho JavaScript cargan el contenido dinámicamente. Los evaluadores suelen encontrarse con pruebas inestables porque los elementos aparecen y desaparecen de forma asíncrona, y no saben cuándo la página está realmente "lista".

Qué deben hacer los desarrolladores:

  • Usa data-testid para estados de carga:
    <div data-testid="loading-spinner"> 
  • Marcar secciones de contenido con atributos de estado: aria-busy="true" durante la carga
  • Asegúrese de que la aplicación alcance un estado estable antes de responder a las interacciones del usuario.
  • Proporcionar señales explícitas de finalización de la carga

Ejemplo real:

// ❌ BAD - No semantic meaning 

<div onClick={() => setSelected(!selected)}> 

<span>Enable notifications</span> 

</div> 

 

// ✅ GOOD - Semantic and testable 

<label htmlFor="notifications-toggle"> 

<input  

id="notifications-toggle"  

type="checkbox"  

data-testid="notifications-toggle" 

/> 

Enable notifications 

</label>

Código de automatización del probador (Dramaturgo):

// Wait for loading to complete 

await page.locator('[data-testid="loading-spinner"]').waitFor({ state: 'hidden' }); 

 

// Now interact with the list 

const userList = page.locator('[data-testid="user-list"]'); 

await expect(userList).toBeVisible();

Diseñar formularios con un manejo de entrada claro. 

El problema: 

Los formularios complejos con estados de error poco claros, validaciones ocultas o comportamientos de entrada no estándar confunden a los scripts de automatización. 

Qué deben hacer los desarrolladores: 

  • Utilice elementos de formulario HTML estándar con atributos de nombre claros. 
  • Proporcione mensajes de error explícitos con data-testid marcadores 
  • Asegúrese de que la validación del formulario sea observable (no que los fallos sean silenciosos). 
  • Incluir confirmaciones de éxito 

Ejemplo real: 

// ✅ GOOD - Clear form with observable states 

<form data-testid="login-form"> 

<div> 

<label htmlFor="email-input">Email:</label> 

<input 

id="email-input" 

name="email" 

type="email" 

data-testid="email-input" 

required 

/> 

{errors.email && ( 

<span data-testid="email-error" role="alert"> 

{errors.email} 

</span> 

)} 

</div> 

 

<div> 

<label htmlFor="password-input">Password:</label> 

<input 

id="password-input" 

name="password" 

type="password" 

data-testid="password-input" 

required 

/> 

</div> 

 

<button  

type="submit"  

data-testid="login-submit-button" 

disabled={isSubmitting} 

> 

{isSubmitting ? 'Logging in...' : 'Login'} 

</button> 

 

{submitSuccess && ( 

<div data-testid="login-success-message" role="status"> 

Login successful! Redirecting... 

</div> 

)} 

</form>

Código de automatización del probador (Selenium):

from selenium import webdriver 

from selenium.webdriver.common.by import By 

from selenium.webdriver.support.ui import WebDriverWait 

from selenium.webdriver.support import expected_conditions as EC 

 

driver = webdriver.Chrome() 

driver.get("https://app.com/login") 

 

# Fill form 

driver.find_element(By.CSS_SELECTOR, '[data-testid="email-input"]').send_keys("test@example.com") 

driver.find_element(By.CSS_SELECTOR, '[data-testid="password-input"]').send_keys("password123") 

 

# Submit 

driver.find_element(By.CSS_SELECTOR, '[data-testid="login-submit-button"]').click() 

 

# Wait for success message 

WebDriverWait(driver, 10).until( 

EC.presence_of_element_located((By.CSS_SELECTOR, '[data-testid="login-success-message"]')) 

)

Logre respuestas de API predecibles y consistentes.

El problema:

Las aplicaciones móviles y web que dependen de las respuestas de la API necesitan estructuras de datos estables. Las respuestas inconsistentes o impredecibles de la API provocan que las pruebas fallen aleatoriamente.

Qué deben hacer los desarrolladores:

  • Utilice una estructura JSON coherente en todos los puntos finales.
  • Incluir identificadores únicos (ID) en cada objeto
  • Documentar contratos de API (OpenAPI/Swagger)
  • Devuelve códigos de estado HTTP y mensajes de error significativos.
  • Prueba las respuestas de la API durante el desarrollo.

Ejemplo real:

// ✅ GOOD - Predictable API response 

{ 

"success": true, 

"data": { 

"id": "user-123", 

"email": "test@example.com", 

"name": "John Doe", 

"created_at": "2026-03-01T10:00:00Z" 

}, 

"timestamp": "2026-03-22T14:30:00Z" 

} 

 

// ❌ BAD - Inconsistent response 

{ 

"status": "ok", 

"user": { 

"uid": "123", 

"email_address": "test@example.com" 

}, 

"meta": { "time": 1679574600 } 

}

Automatización móvil del probador (Appium):

# With predictable API, testers can mock and validate 

from appium import webdriver 

 

driver = webdriver.Remote("http://localhost:4723/wd/hub", capabilities) 

 

# Mock the API response 

mock_response = { 

"success": True, 

"data": { 

"id": "user-123", 

"name": "Test User" 

} 

} 

 

# Now reliably access user data in the app 

user_name_element = driver.find_element("xpath", "//text[@value='Test User']")

Proporcionar configuraciones y modos que faciliten las pruebas.

El problema:

Las funciones de producción, como el análisis de datos, los anuncios, los rastreadores de terceros y los retrasos en la red, pueden interferir con la ejecución de las pruebas.

Qué deben hacer los desarrolladores:

  • Proporcione un modo de prueba o una variable de entorno.
  • Permitir la desactivación de análisis, anuncios y seguimiento en entornos de prueba.
  • Permite omitir animaciones o retrasos innecesarios.
  • Proporcione puntos finales de API para restablecer el estado de la aplicación para realizar pruebas.

Ejemplo real (React):

// ✅ GOOD - Test-friendly configuration 

const isTestMode = process.env.REACT_APP_TEST_MODE === 'true'; 

 

function App() { 

return ( 

<div> 

{!isTestMode && <Analytics />} 

{!isTestMode && <Ads />} 

<MainContent /> 

</div> 

); 

}

Variable de entorno:

REACT_APP_TEST_MODE=true npm start

Expectativas sobre la automatización de documentos

El problema:

Los evaluadores dedican tiempo a realizar ingeniería inversa para descubrir cómo interactuar con una aplicación, especialmente con los componentes personalizados.

Qué deben hacer los desarrolladores:

  • Comparación de data-testid convenciones de nomenclatura en su README
  • Proporcione una guía de prueba/automatización para funciones complejas.
  • Compartir la documentación de los componentes (Storybook, etc.)
  • Enumera todos los eventos personalizados y las funciones de devolución de llamada que los evaluadores deben conocer.

Ejemplo de documentación:

## Automation Testing Guide 

 

### Naming Conventions 

- Buttons: `data-testid="[feature]-[action]-button"` (e.g., `user-logout-button`) 

- Form inputs: `data-testid="[form]-[field]-input"` (e.g., `login-email-input`) 

- Lists: `data-testid="[list]-container"` and items as `[list]-item-[id]` 

 

### Key Components 

- **Modal**: `.modal-overlay` (hidden when closed), contains `data-testid="modal-close-button"` 

- **Dropdown**: Use semantic `<select>` or `aria-haspopup="listbox"` 

- **Date Picker**: Supports keyboard navigation and `aria-label` 

 

### Test Environment Setup 

Set `API_URL=http://localhost:3001/api` to use mock server

Conclusiones clave para los desarrolladores

Lo que necesitan los evaluadores Qué deben hacer los desarrolladores
Identificadores de elementos estables Utilice los atributos data-testid.
Jerarquía de componentes clara Utilice HTML semántico
Estados de carga/listo observables Proporcione indicadores de carga con identificadores.
Comportamiento de forma predecible Validación de documentos y uso de elementos estándar
Respuestas API consistentes Siga los contratos de API y la estructura del documento.
Entorno propicio para las pruebas Admite el modo de prueba, desactiva los anuncios/análisis.
Orientación clara sobre automatización Documente sus expectativas de automatización.

Conclusión

Crear aplicaciones preparadas para la automatización no se trata de sortear limitaciones, sino de diseñar aplicaciones correctamente desde el primer día.

Cuando los desarrolladores priorizan la claridad, la coherencia y la observabilidad, todos ganan: los evaluadores escriben pruebas más rápidas y fiables; los usuarios obtienen software de mayor calidad y todo el equipo trabaja con mayor rapidez.

¿La buena noticia? La mayoría de estas prácticas son, en realidad, buenas prácticas de ingeniería de software. El HTML semántico, las API predecibles y el manejo claro de errores benefician la accesibilidad, el rendimiento y la experiencia del usuario, no solo las pruebas automatizadas.

Comience con data-testid Utiliza atributos, HTML semántico, visualiza los estados de carga y documenta tus componentes. Tus evaluadores de automatización te lo agradecerán y tu aplicación mejorará gracias a ello.

Recursos y herramientas

También puede interesarle