Sesión 3 de mi lab de Selenium + Java: pom.xml, Logback, estructura de carpetas
Configuré pom.xml, Logback y estructuré carpetas base/pages/utils/test. El framework empieza a tomar forma.
Dependencias actualizadas (pom.xml)
Le he añadido cosas al pom.xml
Qué agregué y por qué
| Elemento | Qué es | Para qué sirve |
|---|---|---|
| Logback | Librería de logging | Registrar acciones, errores, timestamps (como el Test Log de TestComplete) |
| maven-surefire-plugin | Plugin de Maven | Ejecutar tests desde terminal con mvn test (sin abrir IntelliJ) |
| Properties centralizadas | Variables en el pom | Definir versiones arriba (<selenium.version>4.27.0</selenium.version>) y usarlas abajo (${selenium.version}). Cambiar en UN lugar actualiza todo. |

El pom.xml completo
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cesar.qa</groupId>
<artifactId>selenium-java</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- Versiones centralizadas -->
<selenium.version>4.27.0</selenium.version>
<testng.version>7.10.2</testng.version>
<webdrivermanager.version>5.9.2</webdrivermanager.version>
<logback.version>1.4.14</logback.version>
</properties>
<dependencies>
<!-- Selenium WebDriver -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
</dependency>
<!-- WebDriverManager - gestión automática de drivers -->
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>${webdrivermanager.version}</version>
</dependency>
<!-- TestNG - framework de testing -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
<!-- Logback - logging profesional (incluye SLF4J) -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Plugin para ejecutar tests con Maven -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
</plugins>
</build>
</project>Ahora explico con detalle estos 3 añadidos.
Explico algunos conceptos que pueden ser nuevos si venís de otras herramientas como TestComplete.
Logback
¿Qué es "logging"?
Logging = dejar registro de lo que hace tu programa mientras se ejecuta.
Es como un diario que escribe tu código automáticamente:
[10:15:23] Usuario intentó hacer login
[10:15:24] Se hizo click en el botón "Enviar"
[10:15:25] ERROR: No se encontró el elemento
[10:15:26] Test terminadoSi conocés JavaScript, es similar a console.log(), console.error(), console.warn(). En Java, el equivalente básico sería System.out.println(), pero eso es muy limitado: no tiene timestamps, niveles de severidad, ni capacidad de guardar en archivos.
Por eso existen librerías de logging profesionales.
El problema: muchas librerías de logging en Java
En Java hay varias librerías para logging (Log4j, Logback, java.util.logging, etc.). Cada una se usa diferente.
Si escribís tu código usando Log4j directamente:
import org.apache.log4j.Logger; // ← Código atado a Log4jY después querés cambiar a Logback (por seguridad, performance, o lo que sea), tendrías que modificar TODOS los archivos donde usaste Log4j. En un proyecto grande, eso puede ser 50 o 100 archivos.
La solución: SLF4J + Logback
SLF4J (Simple Logging Facade for Java) no hace logging. Es una interfaz estándar, un "intermediario".
SIN SLF4J (acoplado):
Tu código → Log4j (directo)
Si querés cambiar: modificar todos los archivos
CON SLF4J (desacoplado):
Tu código → SLF4J (interfaz) → Logback (implementación)
Si querés cambiar: modificar solo pom.xmlLogback es la implementación real que hace el logging. Es moderna, rápida, y muy usada en la industria.
En el pom.xml solo necesitás agregar Logback, que ya incluye SLF4J automáticamente:
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.14</version>
</dependency>¿Vas a cambiar esto más adelante? Probablemente no. SLF4J + Logback es el estándar de la industria. La combinación más usada en proyectos Java profesionales.
mvn test
Es un comando de Maven para ejecutar tests desde la terminal (sin IntelliJ).
# En la carpeta del proyecto:
mvn test¿Para qué sirve?
- Ejecutar tests en servidores de CI/CD (Jenkins, GitHub Actions) ->futuro del framework
- Ejecutar tests sin abrir IntelliJ
- Automatizar ejecuciones
El plugin que lo habilita:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>Sin este plugin, mvn test no sabe qué tests ejecutar ni cómo.
Properties centralizadas
Exactamente lo que pensás. Definís las versiones UNA vez arriba, y las usás abajo con ${nombre}.
<!-- DEFINICIÓN (arriba en el pom) -->
<properties>
<selenium.version>4.27.0</selenium.version>
<testng.version>7.10.2</testng.version>
.
.
.
</properties>
<!-- USO (abajo en dependencies) -->
<dependency>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version> <!-- Usa el valor de arriba -->
</dependency>
<dependency>
<artifactId>testng</artifactId>
<version>${testng.version}</version> <!-- Usa el valor de arriba -->
</dependency>
.
.
.¿Por qué es útil?
<!-- SIN centralizar: si Selenium aparece en 3 lugares -->
<version>4.27.0</version> <!-- Lugar 1 -->
<version>4.27.0</version> <!-- Lugar 2 -->
<version>4.27.0</version> <!-- Lugar 3 -->
<!-- Para actualizar: cambiar en 3 lugares (fácil equivocarse) -->
<!-- CON centralizar: -->
<selenium.version>4.28.0</selenium.version> <!-- Cambio UNA vez -->
<!-- Todos los ${selenium.version} se actualizan automáticamente -->Estructura de carpetas y convención de paquetes
Antes de crear los archivos Java, es importante entender cómo se organizan.
Estructura completa del proyecto
selenium-java/
├── src/
│ ├── main/
│ │ └── java/
│ │ └── com/
│ │ └── cesar/
│ │ └── qa/
│ │ ├── base/
│ │ ├── config/
│ │ ├── pages/
│ │ └── utils/
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── cesar/
│ │ └── qa/
│ │ └── tests/
│ │ └── PrimerTest.java
│ └── resources/
│ └── logback.xml
├── .gitignore
└── pom.xml¿Por qué com.cesar.qa?
Es una convención de Java para nombrar paquetes (packages). Se usa el dominio invertido:
com.cesar.qa
│ │ │
│ │ └── Área del proyecto (qa, web, api, etc.)
│ │
│ └── Tu nombre o empresa (cesar, google, amazon)
│
└── Dominio invertido (com, org, io, dev)¿Por qué dominio invertido?
Para evitar colisiones de nombres. Si dos personas crean una clase ConfigReader:
com.cesar.qa.config.ConfigReader ← Tu clase
com.juan.qa.config.ConfigReader ← Clase de otra persona
org.apache.commons.ConfigReader ← Clase de ApacheCada una es única porque el paquete completo (el "nombre completo" de la clase) es diferente.
Ejemplos de empresas reales
| Empresa | Paquete base |
|---|---|
com.google.gmail | |
| Apache Selenium | org.openqa.selenium |
| TestNG | org.testng |
| Este proyecto | com.cesar.qa |
¿Es obligatorio usar esta convención?
No. Podrías usar qa.base, miproyecto.pages, etc. Pero com.tudominio.proyecto es el estándar en la industria. Si ves código Java profesional, casi siempre sigue este patrón.
Cómo crear la estructura en IntelliJ
- Dentro de
src/main/java(carpeta azul), click derecho → New → Package - Escribir:
com.cesar.qa.base→ Enter - Repetir para:
com.cesar.qa.config,com.cesar.qa.pages,com.cesar.qa.utils
Para test:
- Dentro de
src/test/java, click derecho → New → Package - Escribir:
com.cesar.qa.tests→ Enter
Importante: Las carpetas deben estar DENTRO de java (la carpeta azul). Si las creás afuera, Java no las reconoce.
Estructura del proyecto (qué va en cada carpeta)
Una vez definido el paquete base (com.cesar.qa), el siguiente paso fue organizar el proyecto por responsabilidades, no por casualidad.
Esta es la estructura inicial:

base/ — infraestructura común
Acá vive el core del framework.
Todo lo que:
- envuelve Selenium
- maneja el WebDriver
- define comportamientos base reutilizables
Ejemplos típicos:
BasePage→ wrapper de Selenium (click, type, waits, etc.)BaseTest(más adelante) → setup y teardown de testsDriverManager→ creación y manejo del driver
config/ — configuración centralizada
Responsable de leer y exponer configuración, sin lógica de tests.
Qué va acá:
- URLs
- credenciales
- browser
- flags de ejecución (local / CI)
Ejemplo:
ConfigReaderconfig.properties
👉 Idea clave: si mañana cambiás un entorno, tocás un solo lugar.
pages/ — Page Object Model
Acá viven las pantallas de la aplicación, no los tests.
Cada clase representa una página o sección:
LoginPageDashboardPage- etc.
Responsabilidad:
- localizar elementos
- ejecutar acciones de la UI
- NO hacer asserts de test
utils/ — helpers genéricos
Esta carpeta no es un cajón de sastre.
Solo contiene utilidades:
- genéricas
- transversales
- sin conocimiento de Selenium ni de páginas
Ejemplos válidos:
DateUtilsRandomUtils- helpers de archivos o strings
👉 Si algo empieza a crecer mucho, probablemente no pertenece a utils.
tests/ — casos de prueba
Acá viven los tests en sí, no la lógica.
Responsabilidad:
- orquestar escenarios
- usar pages y base
- definir asserts
Ejemplo:
PrimerTest
👉 El test lee como un guion, no como código técnico.
resources/ — configuración y soporte
Archivos que no son código Java:
logback.xml- properties
- configuraciones de ejecución
👉 Separar código de configuración hace el proyecto más mantenible y portable.
Por qué esta estructura
Esta organización no es casual:
- evita duplicación
- facilita escalar el framework
- permite ejecutar en local o CI sin tocar código
- hace el proyecto legible para otros QAs / devs
No es la estructura final, pero sí una base sólida y profesional sobre la que seguir construyendo.
logback.xml — Configuración del logging
Este archivo controla CÓMO se muestran los logs en consola. Sin este archivo, Logback funciona con configuración por defecto. Con este archivo, controlás el formato, la verbosidad, y dónde se guardan los logs.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- Formato de salida en consola -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- Reducir verbosidad de Selenium -->
<logger name="org.openqa.selenium" level="INFO"/>
<!-- Tu código: mostrar todo incluyendo DEBUG -->
<logger name="com.cesar.qa" level="DEBUG"/>
<!-- Nivel por defecto -->
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>Qué hace cada parte
| Elemento | Función |
|---|---|
appender name="STDOUT" | Define que los logs van a la consola |
pattern | El formato de cada línea de log |
logger name="org.openqa.selenium" | Reduce el ruido de Selenium (solo INFO o más grave) |
logger name="com.cesar.qa" | Tu código muestra TODO, incluyendo DEBUG |
root level="INFO" | Configuración por defecto para todo lo demás |
El pattern explicado
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
| Código | Qué muestra | Ejemplo |
|---|---|---|
%d{HH:mm:ss.SSS} | Hora con milisegundos | 10:15:23.456 |
[%thread] | Hilo de ejecución | [main] |
%-5level | Nivel (INFO, WARN, ERROR) | INFO |
%logger{36} | Clase (máximo 36 caracteres) | c.cesar.qa.pages.LoginPage |
%msg | Tu mensaje | Click en botón login |
%n | Salto de línea |
Niveles de logging
De menos grave a más grave:
DEBUG → INFO → WARN → ERRORSi configurás level="INFO", significa:
- DEBUG: ❌ oculto
- INFO: ✅ visible
- WARN: ✅ visible
- ERROR: ✅ visible
Por eso org.openqa.selenium está en INFO (oculta sus DEBUG que son mucho ruido), y com.cesar.qa está en DEBUG (muestra todo de tu código).
Verificando la configuración
En este punto, con el nuevo pom.xml y logback.xml, ejecutamos PrimerTest.java (modificado para abrir https://demo.serenity.is/Account/Login/) para verificar que todo funciona.
Click derecho en PrimerTest.java → Run
Log de ejecución

El test pasó: Total tests run: 1, Passes: 1, Failures: 0
Nota sobre los warnings en consola
Vi warnings como:
ADVERTENCIA: Unable to find CDP implementation matching 143Esto NO es un error. Es un warning de Selenium porque la versión de Chrome (143) es más nueva que los módulos CDP (Chrome DevTools Protocol) incluidos en Selenium 4.27.
- El test funciona perfectamente
- Es solo ruido cosmético
- Desaparecerá cuando Selenium se actualice para soportar Chrome 143
Lo importante es:
- ✅
Tests passed: 1 - ✅
Failures: 0 - ✅
exit code 0
🔗 Todo el código de esta serie está en: github.com/cesarbeassuarez/qa-automation-lab
📂 selenium-java
Siguiente paso (Día 4): empezar con BaseTest + DriverManager para evitar duplicar setup en cada test.
—
Temas conectados:
Día 1 de este lab y Día 2 de este lab de Selenium con java.