Ámbito y elevación de variables en JavaScript para ingenieros de automatización de pruebas
Parte 8 para quienes aprenden QA: ámbito global, de función y de bloque; var vs let vs const; elevación de declaraciones; la zona muerta temporal; y cómo estas reglas evitan que se filtre la información de las pruebas.
Fuentes: JavaScript For QA Engineers: My Learning Journey — Part 8
A veces JavaScript no falla porque la lógica esté incorrecta. Falla porque una variable no está disponible donde se espera que esté.
Hasta ahora
Un camino típico en JavaScript para la automatización de pruebas comienza con la elección del lenguaje, seguido de variables, tipos, arrays, objetos, operadores, bucles y funciones. El ámbito de definición y el mecanismo de elevación son los conceptos que explican por qué un valor funciona en un lugar y genera error en otro, y por qué el motor parece “ver” las declaraciones antes de la línea en la que se escriben.
¿Qué es el ámbito de definición?
El ámbito de definición responde a una pregunta simple: ¿dónde es visible esta variable o función?
let browser = "Chrome";
console.log(browser);
//Output: Chrome
Aquí browser está disponible en el ámbito donde fue declarado.
Declárelo dentro de una función en su lugar:
function runTest() {
let browser = "Chrome";
console.log(browser);
}
runTest();
Ahora browser solo existe dentro de esa función. Al intentar leerlo fuera de ella:
console.log(browser);
se lanza, ya que el nombre pertenece al ámbito de la función. Esa diferencia es la razón por la cual los tipos de ámbito son importantes en el código de pruebas.
Tipos de ámbito en JavaScript
Las formas que aparecen con más frecuencia en la automatización son:
- Ámbito global
- Ámbito de función
- Ámbito de bloque
- Ámbito léxico
1. Ámbito global
Una variable declarada fuera de todas las funciones y bloques se encuentra en el ámbito global.
const environment = "staging";
function runTest() {
console.log(`Running on: ${environment}`); // Works ✅
}
runTest();
console.log(environment); // Works ✅
Los valores globales son adecuados para datos compartidos como direcciones URL base, nombres de entorno y configuraciones. Úsalos con cuidado: cuando muchos auxiliares modifican el mismo valor global, resulta difícil aislar los fallos.
2. Ámbito de función
Los nombres declarados dentro de una función suelen ser visibles únicamente allí.
function login() {
const password = "Admin@123";
console.log(`Logging in with: ${password}`); // Works ✅
}
login();
console.log(password); // ❌ Error — password is not defined
Esa contención evita que las credenciales y los datos específicos de cada prueba se filtren o sean sobrescritos por auxiliares no relacionados.
Método sencillo para recordarlo
Declarado dentro de una función → está disponible solo dentro de esa función.
Los auxiliares reutilizables para inicio de sesión o navegación dependen de este límite.
3. Alcance de bloque
Un bloque es cualquier cosa entre { }. Con let o const, un nombre declarado en un bloque solo existe dentro de ese bloque.
if (true) {
let browser = "Chrome";
console.log(browser);
}
//Output:Chrome
Acceso fuera del bloque:
console.log(browser);
ocurren errores, ya que let y const tienen alcance de bloque. Ese comportamiento es importante en condicionales y bucles dentro de pruebas.
Ejemplo con un bucle
for (let i = 1; i <= 3; i++) {
console.log(i);
}
//Output:
1
2
3
La asociación let de cada iteración permanece en el bloque de esa iteración, lo que evita la sorpresa típica del uso compartido de var al asignar controladores asíncronos en bucles.
var vs let vs const y alcance
var tiene ámbito de función (o global cuando se declara fuera de una función). let y const tienen ámbito de bloque. Prefiera const por defecto, let cuando sea necesaria la reasignación, y evite usar var en el código de pruebas moderno. En proyectos grandes con Playwright o Cypress, esta regla por sí sola elimina toda una categoría de fugas del tipo “funciona en mi máquina”, donde una especificación sobrescribe el estado de otra a través de un var con ámbito de función compartido.
if (true) {
var browser = "Chrome";
let version = "Latest";
const environment = "QA";
}
console.log(browser);
//Output:Chrome
console.log(version);
console.log(environment);
const baseURL = "https://myapp.com"; // Global — shared everywhere
¿Qué es el elevamiento?
El elevamiento es la costumbre del motor de procesar las declaraciones antes de ejecutar el cuerpo de arriba hacia abajo. Las declaraciones se registran temprano; las asignaciones, en cambio, ocurren cuando se ejecuta su línea correspondiente.
Elevamiento con var
function loginTest() {
const username = "admin"; // Function scope — only here
const password = "Admin@123"; // Function scope — only here if (username === "admin") {
let accessLevel = "full"; // Block scope — only in this if block
console.log(`Access: ${accessLevel}`);
} console.log(`${baseURL}/login`); // Global still accessible ✅
}
El nombre de var existe desde el inicio del ámbito de la función/global (inicializado como undefined) hasta que se ejecuta la línea de asignación.
Elevación con let
let browser = "Chrome";
function runTest() {
console.log(browser);
}
runTest();
//Output:Chrome
let también se registra temprano, pero no se puede leer antes de su línea de declaración. Ese intervalo es la zona muerta temporal.
¿Qué es la zona muerta temporal?
La TDZ es el período desde la entrada al ámbito hasta que se ejecuta la declaración de let/const. El acceso en la TDZ genera un error en lugar de devolver undefined.
let environment = "QA";
function runTest() {
let browser = "Chrome";
if (true) {
let testName = "Login Test";
console.log(environment);
console.log(browser);
console.log(testName);
}
}
runTest();
Método sencillo para recordarlo
Acceso temprano a
var→undefined. Acceso temprano alet/const→ error (TDZ).
Elevación con declaraciones de función
Las declaraciones de función se elevan completamente: se pueden llamar antes de que aparezcan en el archivo.
console.log(browser);
var browser = "Chrome";
// Output:undefined
Expresiones de función y elevación
Las expresiones de función asignadas a const/let siguen las reglas de las variables: el enlace se encuentra en la TDZ hasta que se realiza la asignación.
var browser;
console.log(browser);
browser = "Chrome";
Declaración de función
undefined
Son útiles para auxiliares que se desean poder llamar desde cualquier lugar del ámbito una vez que este comienza.
Expresión de función
console.log(browser);
let browser = "Chrome";
Son más seguras cuando se desea controlar cuándo aparece el valor de la función, a menudo con const.
Ejemplo de automatización de pruebas
Los errores de ámbito se manifiestan claramente en el código de objetos de página y en los fixtures.
ReferenceError
{
console.log(testName);
let testName = "Login Test";
}
Mantener los selectores y las credenciales dentro de las funciones o bloques que los poseen evita la contaminación entre pruebas.
Por qué el ámbito es importante en la automatización
Las variables globales mutables compartidas hacen que las pruebas paralelas o reordenadas sean inestables. El ámbito de función y bloque mantiene los datos de cada prueba de forma local. El ámbito léxico permite que las funciones auxiliares anidadas tengan acceso a la configuración sin tener que pasar nuevamente todos los argumentos.
let testName = "Login Test";
El ámbito léxico en la práctica
Las funciones internas ven las variables definidas en los ámbitos externos donde fueron creadas, no donde se llamaron.
ReferenceError
runTest();
function runTest() {
console.log("Test started");
}
//Output:Test started
Por eso una función de callback definida dentro de una prueba aún puede leer las variables page o user de dicha prueba. Las clausuras son la forma en que los fixtures transmiten contexto a funciones auxiliares anidadas sin necesidad de variables globales.
Errores comunes
Leer una variable declarada con let antes de su declaración:
runTest();
const runTest = function() {
console.log("Test started");
};
Suponer que var tiene ámbito de bloque dentro de un if:
ReferenceError
Olvidar que un bucle con var comparte un mismo enlace entre las iteraciones:
runTest();
function runTest() {
console.log("Test started");
}
Depuración de problemas de ámbito
Cuando un ReferenceError indique una variable que acaba de definir, verifique a qué { } o función le pertenece, si utilizó var en lugar de let, y si el acceso se produce antes que la declaración (TDZ).
runTest();
const runTest = function() {
console.log("Test started");
};
Reglas prácticas para conjuntos de pruebas
- Preferir
constpara páginas, selectores y objetos de configuración. - Usar
letsolo para valores que deben cambiar. - Evitar el uso de
var. - Mantener los datos confidenciales dentro del ámbito de la función.
- No depender de variables globales accidentales creadas por declaraciones faltantes.
const browser = "Chrome";
function runTest() {
const testName = "Login Test";
console.log(`Running ${testName}`);
console.log(`Browser: ${browser}`);
}
runTest();
//Output:
Running Login Test
Browser: Chrome
let testStatus = "Passed";
function testOne() {
testStatus = "Failed";
}
function testTwo() {
console.log(testStatus);
}
Modelo mental rápido
Ámbito = dónde es visible un nombre. Elevación = momento en que se registra la declaración. TDZ = el rango prohibido antes de que let/const inicialicen.
const testCases = ["Login", "Search", "Checkout"];
function executeTests() {
for (const testCase of testCases) {
console.log(`Executing: ${testCase}`);
}
}
executeTests();
//Output:
Executing: Login
Executing: Search
Executing: Checkout
Combinándolo todo en una pequeña ayuda de prueba
let username;
let password;
let browser;
let testStatus;
function login() {
const username = "qa_user";
const password = "Password@123"; console.log(`Logging in as ${username}`);}
Con esos marcadores de posición en su lugar, las variables locales de la ayuda permanecen dentro de ella, la configuración a nivel de suite sigue siendo intencional, y los errores por acceso prematuro se vuelven comprensibles en lugar de misteriosos. El ámbito y la elevación dejan de ser trivialidades para convertirse en herramientas cotidianas para la automatización estable de JavaScript.
Cadenas de ámbito durante los pasos de prueba asíncrona
La automatización rara vez permanece sincrónica. Cuando una prueba async espera un clic y luego lee una variable, el enlace proviene aún del ámbito léxico donde se definió la función. El elevamiento de declaraciones no mueve las asignaciones más allá de los puntos await; solo afecta cómo se registran las declaraciones al comenzar el cuerpo de la función. Mantener los datos de los fixtures en enlaces const al principio de la función de prueba facilita seguir ese modelo mental cuando las pilas se despliegan tras los tiempos de espera.
Por qué los errores TDZ parecen severos — y por qué son útiles
Ver un ReferenceError en lugar de undefined puede sorprender a los ingenieros acostumbrados al código con var. En un conjunto de pruebas, esa severidad es útil: falla rápidamente cuando un helper se coloca antes de su configuración con let. Considere los fallos TDZ como errores de ordenamiento en el archivo de pruebas, y no como comportamientos impredecibles del producto.
Migrar suites antiguas con var
Al actualizar la automatización heredada, reemplace var por const o let de forma gradual, ejecute la suite y vigile posibles problemas de ámbito de bloque dentro de los cuerpos de if/for que anteriormente causaban fugas. Combine esta migración con reglas de linteo que prohíban el uso de var, para que las pruebas nuevas no reintroduzcan fugas a nivel de función.
Uso de shadowing en ayudantes anidados
Un const browser = „Firefox“ interno dentro de una función auxiliar oculta un browser externo durante la duración de ese bloque. Eso es sombreado léxico, no elevación. En los objetos de página, el sombreado puede hacer que una prueba lea la URL base incorrecta si una función auxiliar anidada vuelve a declarar un nombre conocido. Prefiera nombres distintos (overrideBrowser, tempBrowser) al anidar funciones, o pase valores como parámetros en lugar de reutilizar y volver a declarar el mismo identificador.
Lecturas relacionadas
- Closures, elevación y this: reglas de JavaScript que aparecen en errores — Explicación detallada sobre la elevación, los closures y el this a través de errores relacionados con bucles y manejadores que ilustran cómo se aplican estas reglas: TDZ, enlaces compartidos de variables y métodos desacoplados.