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.

Estructura de carpetas del proyecto selenium-java en IntelliJ IDEA con anotaciones explicativas.
Estructura inicial del proyecto con responsabilidades definidas por carpeta.

Dependencias actualizadas (pom.xml)

Le he añadido cosas al pom.xml

Qué agregué y por qué

ElementoQué esPara qué sirve
LogbackLibrería de loggingRegistrar acciones, errores, timestamps (como el Test Log de TestComplete)
maven-surefire-pluginPlugin de MavenEjecutar tests desde terminal con mvn test (sin abrir IntelliJ)
Properties centralizadasVariables en el pomDefinir versiones arriba (<selenium.version>4.27.0</selenium.version>) y usarlas abajo (${selenium.version}). Cambiar en UN lugar actualiza todo.
Comparación lado a lado del pom.xml: a la izquierda con properties centralizadas usando variables como ${selenium.version}, a la derecha con versiones hardcodeadas directamente en cada dependencia. Flechas conectan las partes equivalentes mostrando la diferencia de enfoque.
Cambios entre: pom.xml nuevo (izquierda) vs pom.xml viejo (derecha). El objetivo fue dejar el proyecto listo para crecer: logging real, ejecución por terminal, y versiones ordenadas.

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 terminado

Si 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 Log4j

Y 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.xml

Logback 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 Apache

Cada una es única porque el paquete completo (el "nombre completo" de la clase) es diferente.

Ejemplos de empresas reales

EmpresaPaquete base
Googlecom.google.gmail
Apache Seleniumorg.openqa.selenium
TestNGorg.testng
Este proyectocom.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

  1. Dentro de src/main/java (carpeta azul), click derecho → New → Package
  2. Escribir: com.cesar.qa.base → Enter
  3. Repetir para: com.cesar.qa.config, com.cesar.qa.pages, com.cesar.qa.utils

Para test:

  1. Dentro de src/test/java, click derecho → New → Package
  2. 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:

Vista expandida del árbol de carpetas en IntelliJ mostrando src/main/java/com.cesar.qa con subcarpetas base, config, pages, utils, y src/test/java/com.cesar.qa.tests con PrimerTest. Anotaciones verdes explican el propósito de cada carpeta.
Organización por responsabilidades bajo el paquete com.cesar.qa.

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 tests
  • DriverManager → 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:

  • ConfigReader
  • config.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:

  • LoginPage
  • DashboardPage
  • 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:

  • DateUtils
  • RandomUtils
  • 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.

Archivo logback.xml abierto en IntelliJ IDEA mostrando la configuración de logging.
Configuración de Logback: formato de logs, niveles por paquete, salida a consola.
<?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

ElementoFunción
appender name="STDOUT"Define que los logs van a la consola
patternEl 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ódigoQué muestraEjemplo
%d{HH:mm:ss.SSS}Hora con milisegundos10:15:23.456
[%thread]Hilo de ejecución[main]
%-5levelNivel (INFO, WARN, ERROR)INFO
%logger{36}Clase (máximo 36 caracteres)c.cesar.qa.pages.LoginPage
%msgTu mensajeClick en botón login
%nSalto de línea

Niveles de logging

De menos grave a más grave:

DEBUG → INFO → WARN → ERROR

Si 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

IntelliJ IDEA mostrando la ejecución exitosa de PrimerTest.java: código del test a la izquierda, consola con logs de WebDriverManager y Selenium en el centro, resultado final mostrando Total tests run 1, Passes 1, Failures 0.
Test ejecutado correctamente. El setup de pom.xml y logback.xml funciona.

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 143

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