Inicio / Artículos

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

Editor: REACTAPP.TOP7 min de lectura1673 palabras

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:

  1. Ámbito global
  2. Ámbito de función
  3. Ámbito de bloque
  4. Á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 a let/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 const para páginas, selectores y objetos de configuración.
  • Usar let solo 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

  • Master Playwright GitHub Actions YAML para ejecuciones de pruebas CI — Lee un flujo de trabajo de Playwright GitHub Actions de principio a fin: sobre desencadenantes, tareas, descarga de archivos, configuración de Node, npm ci, navegadores, pruebas y generación de informes.