Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
¿Alguna vez abriste un proyecto de automatización y te encontraste con un pom.xml lleno de dependencias que no entendías, errores de compilación crípticos y sin saber por dónde empezar? Si estás iniciando en el mundo del QA con Java, esa sensación es más común de lo que crees.
La buena noticia es que configurar tu entorno de trabajo no tiene que ser un dolor de cabeza. Con Maven, Java 11 y Visual Studio Code puedes tener un proyecto de automatización limpio, estructurado y listo para crecer, en menos de 20 minutos.
En esta guía vas a aprender exactamente eso: crear un proyecto Maven desde cero, configurar las dependencias correctas para el stack SDTE (Selenium + TestNG), y escribir tu primer DriverFactory.java aplicando el patrón Factory Method y el principio SOLID de Inversión de Dependencias. No como algo abstracto, sino como código real que podrás usar desde el primer día.
Al final encontrarás el código completo listo para copiar y un video donde lo construimos en vivo desde una carpeta vacía hasta el primer test completado (PASS).
Antes de escribir una sola línea, vale la pena responder la pregunta que seguramente tienes: ¿por qué Maven?
En el ecosistema QA con Java, Maven sigue siendo el estándar de facto por tres razones concretas:
src/main/java, src/test/java) es predecible en cualquier proyecto del mundo. Entras a un repo nuevo y ya sabes dónde están los tests.mvn test. Cero configuración extra.Gradle es una excelente opción también, pero para un QA Junior que está aprendiendo el stack, Maven reduce la curva de aprendizaje porque su sintaxis XML es explícita: cada dependencia se ve, se entiende y se versiona sin magia detrás.
Antes de crear el proyecto necesitas tener instalado:
| Herramienta | Versión mínima | Verificar con |
|---|---|---|
| Java JDK | 11 (LTS) | java -version |
| Maven | 3.8+ | mvn -version |
| Visual Studio Code | Cualquier reciente | — |
| Extension Pack for Java | Última | Desde el marketplace de VSCode |
Tip para instalar Java 11 rápido: usa SDKMAN en Mac/Linux o Adoptium en Windows. Ambos te permiten tener múltiples versiones de Java sin conflictos.
Para instalar la extensión de Java en VSCode, abre el marketplace (Ctrl+Shift+X), busca Extension Pack for Java de Microsoft e instálala. Esta sola extensión trae todo lo que necesitas: soporte de Maven, autocompletado, debugger y test runner.
Antes de crear archivos, entendamos qué vamos a construir. La estructura de paquetes del stack SDTE para un proyecto inicial es:
sdte-proyecto/
├── pom.xml
└── src/
├── main/
│ └── java/
│ └── com/sdte/
│ └── core/
│ └── DriverFactory.java ← hoy lo creamos aquí
└── test/
└── java/
└── com/sdte/
└── tests/
└── web/
└── SmokeTest.java ← y este también
Esta separación no es capricho: Maven usa src/main para código de producción y src/test para código de pruebas. Cuando ejecutas mvn test, solo compila y corre lo de src/test, pero tiene acceso a todo lo de src/main. Así, tu DriverFactory.java vive en main (es infraestructura reutilizable) y tus tests viven en test.
Abre la paleta de comandos en VSCode (Ctrl+Shift+P), escribe Maven: Create Maven Project y selecciona el arquetipo maven-archetype-quickstart. Cuando te pida los datos, usa:
groupId: com.sdte
artifactId: sdte-proyecto
version: 1.0.0
package: com.sdte
VSCode generará la estructura base automáticamente y abrirá el pom.xml.
Reemplaza el contenido del pom.xml generado con esta configuración lista para el stack SDTE:
<?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.sdte</groupId>
<artifactId>sdte-proyecto</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<!-- Java 11 LTS — estable y ampliamente soportado -->
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- Versiones del stack SDTE -->
<selenium.version>4.20.0</selenium.version>
<testng.version>7.10.2</testng.version>
</properties>
<dependencies>
<!-- Selenium WebDriver — automatización web -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
</dependency>
<!-- TestNG — framework de testing -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Plugin para ejecutar tests TestNG con mvn test -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
</plugin>
</plugins>
</build>
</project>
Guarda el archivo. VSCode detectará los cambios y descargará las dependencias automáticamente (verás el progreso en la barra inferior). Si no arranca solo, haz clic derecho en el pom.xml → Update Project.
Abre la terminal integrada de VSCode (Ctrl+Ñ) y ejecuta:
mvn dependency:resolve
Deberías ver BUILD SUCCESS con la lista de dependencias resueltas. Si ves errores de red, verifica tu conexión o configura el proxy en ~/.m2/settings.xml.
Aquí están los tres archivos que construimos hoy. Están ordenados para que los crees en secuencia y el proyecto compile en el primer intento.
Archivo 1 — DriverFactory.java
Ruta: src/main/java/com/sdte/core/DriverFactory.java
// ============================================
// DriverFactory.java
// Capa : DSL Java / Core
// Patrón : Factory Method
// Principio: D — Dependency Inversion (DIP)
// Nivel : Principiante
// ============================================
// Por qué DIP aquí:
// Los tests deben depender de WebDriver (abstracción),
// NUNCA de new ChromeDriver() (implementación concreta).
// DriverFactory es el único lugar donde vive esa decisión.
// ============================================
package com.sdte.core;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.edge.EdgeDriver;
public class DriverFactory {
// ThreadLocal: cada hilo de ejecución tiene su propio driver.
// Esto permite correr tests en paralelo sin conflictos.
private static final ThreadLocal<WebDriver> driverHolder =
new ThreadLocal<>();
/**
* Factory Method — crea y almacena el driver según el browser.
* Llama a este método una sola vez por hilo (en @BeforeClass).
*
* @param browser "chrome" | "firefox" | "edge"
* @return WebDriver listo para usar
*/
public static WebDriver getDriver(String browser) {
if (driverHolder.get() == null) {
// Paso 1: decidir qué implementación concreta crear
WebDriver driver;
switch (browser.toLowerCase()) {
case "firefox":
driver = new FirefoxDriver();
break;
case "edge":
driver = new EdgeDriver();
break;
case "chrome":
default:
// Chrome es el default — cambia con -Dbrowser=firefox
driver = new ChromeDriver();
break;
}
// Paso 2: almacenar en ThreadLocal para este hilo
driverHolder.set(driver);
}
// Paso 3: retornar la instancia (siempre WebDriver, nunca ChromeDriver)
return driverHolder.get();
}
/**
* Cierra el browser y libera el ThreadLocal.
* Llama a este método en @AfterClass, siempre.
*/
public static void quitDriver() {
if (driverHolder.get() != null) {
driverHolder.get().quit();
// Evitar memory leak en ejecuciones paralelas con grid
driverHolder.remove();
}
}
}
Archivo 2 — SmokeTest.java
Ruta: src/test/java/com/sdte/tests/web/SmokeTest.java
// ============================================
// SmokeTest.java
// Capa : TestNG Framework
// Principio: D — Dependency Inversion aplicado
// Nivel : Principiante
// ============================================
// Nota clave:
// Este test NUNCA instancia ChromeDriver directamente.
// Depende de WebDriver (abstracción) y DriverFactory se encarga
// de decidir qué browser usar. Eso es DIP en la práctica.
// ============================================
package com.sdte.tests.web;
import com.sdte.core.DriverFactory;
import org.openqa.selenium.WebDriver;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class SmokeTest {
// Abstracción — no ChromeDriver, no FirefoxDriver: WebDriver
private WebDriver driver;
@BeforeClass
public void setUp() {
// Paso 1: obtener el browser desde property del sistema
// Ejecutar con: mvn test -Dbrowser=firefox
// Si no se pasa la property, usa chrome por defecto
String browser = System.getProperty("browser", "chrome");
driver = DriverFactory.getDriver(browser);
driver.manage().window().maximize();
}
@Test
public void verificarTituloGoogle() {
// Paso 1: navegar
driver.get("https://www.google.com");
// Paso 2: obtener título real de la página
String titulo = driver.getTitle();
// Paso 3: verificar con mensaje descriptivo
Assert.assertTrue(
titulo.contains("Google"),
"El título debería contener 'Google' pero fue: " + titulo
);
}
@AfterClass
public void tearDown() {
// Siempre limpiar — libera el browser y el ThreadLocal
DriverFactory.quitDriver();
}
}
Archivo 3 — ejecutar el test
# Ejecutar con Chrome (default)
mvn test
# Ejecutar con Firefox
mvn test -Dbrowser=firefox
# Ejecutar con Edge
mvn test -Dbrowser=edge
Output esperado en consola:
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS
| Clase | Responsabilidad | Patrón / Principio |
|---|---|---|
DriverFactory | Crear y almacenar el WebDriver correcto | Factory Method + ThreadLocal |
SmokeTest | Verificar comportamiento de la app | Usa abstracción (DIP) |
¿Por qué ThreadLocal? Imagínalo como un casillero: cada hilo de ejecución tiene su propio casillero con su propio driver. Cuando corres tests en paralelo con TestNG, el hilo 1 no puede agarrar el driver del hilo 2. Sin ThreadLocal, dos tests corriendo al mismo tiempo usarían el mismo browser y se pisarían entre sí.
¿Por qué WebDriver y no ChromeDriver en el test? Porque mañana quizás el cliente pide correr los tests en Firefox, o el CI los corre en Edge. Si escribiste ChromeDriver driver, tienes que cambiar cada test. Si escribiste WebDriver driver, solo cambias DriverFactory y todo funciona. Eso es exactamente lo que significa Dependency Inversion: depender de abstracciones, no de implementaciones.
¿Prefieres ver cómo se construye desde una carpeta vacía? En este video lo hacemos en tiempo real:
Timestamps:
0:00 — Intro: qué vamos a construir y por qué0:45 — Instalación de JDK 11 y verificación1:30 — Crear el proyecto Maven desde VSCode3:00 — Configurar el pom.xml con Selenium + TestNG5:00 — Escribir DriverFactory.java — patrón Factory Method explicado8:30 — Escribir SmokeTest.java — aplicando DIP11:00 — mvn test en vivo — primer test verde13:00 — Cambiar browser con -Dbrowser=firefox sin tocar el test14:30 — Conclusión y próximos pasos del stack SDTERecursos del video:
📎 Código completo en GitHub: [link]
📎 Adoptium JDK 11: https://adoptium.net
📎 Extension Pack for Java: marketplace VSCode
Nivel 1 — Ejecuta (10 min)
Copia los tres archivos tal como están, ejecuta mvn test y observa cómo Chrome se abre, navega a Google y el test pasa. Luego ejecuta mvn test -Dbrowser=firefox y mira cómo el mismo test corre en Firefox sin cambiar una sola línea del test.
Objetivo: confirmar que el entorno funciona y ver DIP en acción
Nivel 2 — Extiende el DriverFactory (20 min)
Agrega soporte para un cuarto browser: Safari (solo Mac). El método getDriver() debe aceptar "safari" y crear un SafariDriver. Verifica que los tests existentes no necesitan ningún cambio.
Objetivo: experimentar que Open/Closed y DIP van de la mano
Nivel 3 — Integra configuración externa (45 min)
En lugar de pasar el browser por -Dbrowser=, lee el valor desde un archivo config/config.properties en src/test/resources. Crea una clase ConfigReader.java en com/sdte/core/ que cargue esa propiedad. DriverFactory debe usar ConfigReader en lugar de System.getProperty().
Objetivo: construir la base real de configuración del stack SDTE
¿Llegaste al nivel 3? Comparte tu ConfigReader.java en los comentarios 👇
Hoy configuraste la base del stack SDTE desde cero. Antes de cerrar, los tres puntos que no se te deben olvidar:
pom.xml hacen que cualquier persona pueda entrar a tu proyecto y orientarse en segundos.DriverFactory es tu primer patrón de diseño: Factory Method + ThreadLocal no es complejidad innecesaria, es la diferencia entre un test suite que escala y uno que explota en paralelo.new ChromeDriver() en un test: cada vez que lo veas en código ajeno, ya sabes que hay una violación de DIP ahí. Ya tienes el criterio para mejorarlo.La próxima semana vamos a construir encima de este setup: agregaremos BasePage.java con el patrón Template Method y crearemos el primer Page Object real. Si no quieres perderte ese post, suscríbete al newsletter abajo.
¿Qué parte implementarías primero en tu proyecto actual: el DriverFactory o el ConfigReader del nivel 3? Cuéntame en los comentarios.