JavaScript Basics for Testers: Variables, Data Types, and Operators
Table of Contents
- Why JavaScript Matters for QA Engineers
- Setting Up Your JavaScript Environment
- Variables in JavaScript: var, let, and const
- JavaScript Data Types Explained
- Operators in JavaScript
- Type Coercion and Truthy/Falsy Values
- Practical JavaScript Exercises for Testers
- Common Mistakes QA Engineers Make with JavaScript
- Key Takeaways
- FAQ
Contents
Why JavaScript Matters for QA Engineers
I have trained over 15,000 students at The Testing Academy, and the number one skill gap I see among manual testers is not test strategy or bug reporting. It is JavaScript. In 2026, 68% of test automation job postings in India list JavaScript or TypeScript as a required or strongly preferred skill. If you are a manual tester in Bangalore, Hyderabad, or Pune looking to move into automation, you cannot skip this.
Playwright, Cypress, WebdriverIO, and even Selenium with WebDriver JS all run on JavaScript. The test scripts you write, the assertions you make, and the data you manipulate inside your automation framework all use JavaScript syntax. Skip the fundamentals, and you will spend hours debugging issues that take minutes to fix if you understand how the language works.
This article is Day 1 of my 21-day Playwright + TypeScript series. We start with the absolute basics: variables, data types, and operators. These three concepts power every line of code you will write in the next 20 days. Master them here, and the rest of the series becomes dramatically easier.
Setting Up Your JavaScript Environment
Before you write a single variable, you need a place to run your code. The good news: you do not need a complex setup. You have three free options that take under two minutes to configure.
Option 1: Browser Console
Open any browser, press F12, and click the Console tab. You can type JavaScript directly and see results instantly. This is the fastest way to experiment with the examples in this article.
Option 2: Node.js
Install Node.js from nodejs.org. Open a terminal and type node to enter the REPL, or create a file called test.js and run it with node test.js. Node.js is what powers Playwright under the hood, so getting comfortable with it now pays off immediately.
Option 3: VS Code
Download VS Code, install the “Code Runner” extension, and you can execute JavaScript files with a single keyboard shortcut. This is the setup I recommend for the full 21-day series because it scales from basic scripts to full Playwright projects.
Variables in JavaScript: var, let, and const
A variable is a named container that stores a value. In JavaScript, you declare variables using three keywords: var, let, and const. Understanding the difference between them is not optional. It is the single most common source of bugs I see in automation scripts written by testers transitioning from manual QA.
var: The Legacy Keyword
var was the only way to declare variables before ES6 (2015). It has two major problems:
- Function scope, not block scope: A
vardeclared inside anifblock orforloop is visible outside that block. This causes unexpected overwrites in automation loops where you iterate over test data. - Hoisting: JavaScript moves
vardeclarations to the top of their scope during compilation, which means you can reference a variable before you declare it and getundefinedinstead of an error.
function runTests() {
if (true) {
var testName = "Login Test";
}
console.log(testName); // Outputs: Login Test
}
In this example, testName leaks outside the if block. In a test framework, this means one test case can accidentally overwrite a variable that another test case relies on. I stopped using var entirely in 2017. You should too.
let: The Modern Standard
let was introduced in ES6 and is now the default choice for variables whose values change. It is block-scoped, which means it only exists inside the curly braces where you declare it.
function runTests() {
if (true) {
let testName = "Login Test";
}
console.log(testName); // Error: testName is not defined
}
This is what you want. Each test step, each loop iteration, and each conditional block gets its own isolated variable. No leaks. No surprise overwrites.
const: For Values That Never Change
const is also block-scoped, but with one additional rule: you cannot reassign the variable after declaration. This does not mean the value is frozen. If you declare an array or object with const, you can still modify the contents. You just cannot point the variable to a new array or object.
const BASE_URL = "https://example.com";
BASE_URL = "https://other.com"; // Error: Assignment to constant variable
const testData = { username: "admin", password: "secret" };
testData.username = "qa_user"; // This works fine
I use const by default for every variable. Only when I know a value will change do I switch to let. This discipline catches bugs early. If I accidentally try to reassign a constant, JavaScript throws an error immediately instead of silently accepting a mistake that breaks my test later.
Variable Naming Rules
JavaScript variable names must follow these rules:
- Must start with a letter, underscore, or dollar sign. Cannot start with a number.
- Case-sensitive:
testNameandtestnameare different variables. - Cannot use reserved words like
class,return, orif.
For automation code, I follow camelCase: userName, expectedResult, isLoggedIn. Descriptive names beat short names every time. btn is bad. submitButton is good. Your future self, reading this code at 11 PM during a production release, will thank you.
JavaScript Data Types Explained
Every value in JavaScript has a type. Understanding these types prevents the silent failures that make automation scripts unreliable. JavaScript has eight data types divided into two categories: primitives and objects.
Primitive Data Types
Primitives are immutable. When you assign a primitive value to a variable, you get a copy of the value, not a reference to it.
String
Text values wrapped in single quotes, double quotes, or backticks. Backticks enable template literals, which let you embed variables directly inside strings.
const testName = "Login Validation";
const errorMsg = 'Invalid credentials';
const fullMsg = `Test "${testName}" failed with error: ${errorMsg}`;
Template literals are essential for dynamic test reporting. You will use them constantly when generating failure messages or logging test steps.
Number
JavaScript has only one numeric type. It handles integers and decimals alike. There is no separate float or double type like in Java.
const timeout = 3000; // integer
const price = 99.99; // decimal
const invalid = "100" / 2; // 50 — JavaScript coerces the string to a number
Be careful with floating-point math. 0.1 + 0.2 does not equal 0.3 in JavaScript. It equals 0.30000000000000004. In assertions, always use approximate comparisons for decimal values or convert to integers first.
Boolean
true or false. Booleans drive conditional logic in your tests: should I run this step? Is the element visible? Did the assertion pass?
const isVisible = true;
const isEnabled = false;
Undefined
A variable that has been declared but not assigned a value is undefined. This is different from null. In automation, undefined often means you tried to access a page element before it loaded, or you referenced a config variable that does not exist.
let testResult;
console.log(testResult); // undefined
Null
null represents an intentional absence of value. You set something to null when you want to clear it. In Playwright, a selector that finds no matching element might return null depending on the API method you use.
let sessionToken = "abc123";
sessionToken = null; // Session cleared
BigInt
For integers larger than Number.MAX_SAFE_INTEGER (9007199254740991). Rarely needed in web testing, but useful if you work with timestamps or IDs that exceed the safe integer limit.
const hugeId = 123456789012345678901234567890n;
Symbol
A unique identifier. Symbols are mostly used in advanced JavaScript patterns and are not critical for test automation. You can skip this for now and revisit it after Day 10 of this series.
Object Data Type
Objects are collections of key-value pairs. They are mutable and passed by reference, not by value. This distinction matters deeply in automation because modifying an object in one place changes it everywhere.
const user = {
username: "test_user",
password: "Test@123",
role: "qa"
};
console.log(user.username); // test_user
user.password = "NewPass456";
Objects are the backbone of test data management. You will store login credentials, API payloads, and configuration settings as objects. Arrays are also objects in JavaScript — specifically, objects with numeric keys and a length property.
const testCases = ["Login", "Checkout", "Search"];
console.log(testCases[0]); // Login
console.log(testCases.length); // 3
typeof Operator
Use typeof to check a value’s type at runtime. This is invaluable for debugging when a test fails because a function received the wrong input type.
console.log(typeof "hello"); // string
console.log(typeof 42); // number
console.log(typeof true); // boolean
console.log(typeof undefined); // undefined
console.log(typeof null); // object (this is a well-known JavaScript bug)
console.log(typeof {}); // object
console.log(typeof []); // object (arrays are objects)
Notice that typeof null returns "object". This bug has existed since the first version of JavaScript in 1995 and will never be fixed because it would break existing code. To check for null specifically, use strict equality: value === null.
Operators in JavaScript
Operators perform actions on values. You need to know five categories to write reliable automation scripts.
Arithmetic Operators
Basic math: +, -, *, /, % (modulo), ** (exponentiation).
const totalTests = 50;
const passedTests = 45;
const failedTests = totalTests - passedTests; // 5
const passRate = (passedTests / totalTests) * 100; // 90
const isEven = 10 % 2 === 0; // true
The modulo operator is particularly useful for looping strategies. If you want to run a cleanup step every 10th test, if (index % 10 === 0) is the pattern you use.
Assignment Operators
Shorthand forms save typing and reduce errors.
let retries = 0;
retries += 1; // Same as retries = retries + 1
retries -= 1; // Same as retries = retries - 1
In retry logic, retries++ and ++retries look similar but behave differently. retries++ returns the value before incrementing. ++retries returns the value after incrementing. I prefer retries += 1 because it is explicit and avoids confusion.
Comparison Operators
This is where most JavaScript bugs in test automation originate. You have two types of equality: loose and strict.
console.log(5 == "5"); // true — loose equality with type coercion
console.log(5 === "5"); // false — strict equality, types must match
console.log(0 == false); // true — dangerous coercion
console.log(0 === false); // false — correct behavior
Always use strict equality (=== and !==) in your assertions. Loose equality (==) performs type coercion, which means the number 0 equals the string "0", which equals the boolean false. This causes tests to pass when they should fail.
Other comparison operators:
console.log(10 > 5); // true
console.log(10 <= 5); // false
console.log("apple" < "banana"); // true — string comparison by Unicode order
Logical Operators
&& (AND), || (OR), and ! (NOT) combine boolean conditions.
const isVisible = true;
const isEnabled = false;
if (isVisible && isEnabled) {
console.log("Can interact");
} else {
console.log("Cannot interact");
}
Short-circuit evaluation is a powerful pattern. a || b returns a if a is truthy, otherwise it returns b. This is the standard way to set default values in JavaScript:
const timeout = userTimeout || 5000; // If userTimeout is falsy, use 5000
However, since ES2020, the nullish coalescing operator ?? is safer. It only falls back for null and undefined, not for 0 or empty strings:
const timeout = userTimeout ?? 5000; // Correct: 0 is a valid timeout value
Ternary Operator
A compact if-else expression:
const status = testPassed ? "PASSED" : "FAILED";
Use ternaries for simple assignments. For multi-line logic, stick to if-else blocks for readability.
Type Coercion and Truthy/Falsy Values
JavaScript converts values between types automatically in certain contexts. This is called type coercion, and it is the silent killer of test reliability.
Falsy Values
These six values evaluate to false in a boolean context:
false0""(empty string)nullundefinedNaN(Not a Number)
Everything else is truthy. Even the string "false" is truthy because it is a non-empty string.
if ("false") {
console.log("This runs!"); // Because "false" is a non-empty string
}
In test automation, this trips up assertions constantly. A function returns an empty string when an element has no text content. Your assertion checks if (elementText) expecting false, but the condition is falsy, so the else branch runs. The logic works by accident, not by design.
String Concatenation vs Addition
When you use + with a string and a number, JavaScript coerces the number to a string:
const result = "5" + 3; // "53", not 8
const sum = 5 + "3"; // "53", not 8
If you build dynamic test data by concatenating values, always convert numbers explicitly:
const userId = 42;
const url = "/users/" + String(userId); // "/users/42"
Explicit Type Conversion
Control the conversion yourself instead of letting JavaScript guess:
const str = "42";
const num = Number(str); // 42
const bool = Boolean(str); // true
const strAgain = String(42); // "42"
Explicit conversion makes your intent clear. Clear intent means fewer bugs and faster debugging.
Practical JavaScript Exercises for Testers
Reading about variables and operators is not enough. You need to write code. Here are four exercises that mirror real automation scenarios.
Exercise 1: Test Result Calculator
const totalTests = 100;
const passedTests = 87;
const failedTests = totalTests - passedTests;
const passRate = (passedTests / totalTests) * 100;
console.log(`Pass Rate: ${passRate.toFixed(2)}%`);
console.log(`Status: ${passRate >= 80 ? "ACCEPTABLE" : "BLOCKING"}`);
Exercise 2: Dynamic URL Builder
const baseUrl = "https://api.example.com";
const endpoint = "/users";
const userId = 42;
const fullUrl = `${baseUrl}${endpoint}/${userId}`;
console.log(fullUrl); // https://api.example.com/users/42
Exercise 3: Retry Logic with Variables
const maxRetries = 3;
let attempt = 0;
let isSuccess = false;
while (attempt < maxRetries && !isSuccess) {
attempt += 1;
console.log(`Attempt ${attempt} of ${maxRetries}`);
// Simulate test execution here
isSuccess = attempt === 2; // Succeeds on second try
}
console.log(`Final status: ${isSuccess ? "PASSED" : "FAILED"}`);
Exercise 4: Configuration Object
const config = {
baseUrl: "https://staging.example.com",
timeout: 10000,
browser: "chromium",
headless: false,
credentials: {
username: "qa_user",
password: "TestPass123"
}
};
console.log(`Running tests on ${config.baseUrl}`);
console.log(`Browser: ${config.browser}`);
console.log(`Timeout: ${config.timeout}ms`);
Run these exercises in your browser console or Node.js REPL. Modify the values. Break them intentionally and observe the errors. This is how you build intuition for the language.
Common Mistakes QA Engineers Make with JavaScript
After reviewing thousands of test scripts from students and mentees, I see the same mistakes repeatedly. Avoid these five patterns, and you will be ahead of 80% of QA engineers who write automation.
Mistake 1: Using var in Modern Code
var leaks scope, gets hoisted, and behaves unpredictably in loops. Replace every var with let or const.
Mistake 2: Loose Equality in Assertions
== performs type coercion. 0 == "0" is true. 0 == false is true. Always use === and !== in test assertions.
Mistake 3: Not Declaring Variables
Assigning to a variable without let, const, or var creates a global variable. This pollutes the global namespace and causes silent failures in test suites where multiple files run in the same process.
Mistake 4: Trusting typeof for Arrays
typeof [] returns "object". To check if something is an array, use Array.isArray(value).
Mistake 5: Ignoring the Difference Between null and undefined
Both are falsy, but they mean different things. undefined means "this was never set." null means "this was intentionally cleared." Use === null to distinguish them when debugging selector failures.
Key Takeaways
- Use
constby default,letwhen reassignment is necessary, and never usevar. This eliminates scope-related bugs in automation loops and conditional blocks. - JavaScript has eight data types: string, number, boolean, undefined, null, BigInt, Symbol, and object. Primitives are copied by value. Objects are passed by reference.
- Always use strict equality (
===,!==). Loose equality (==) performs silent type coercion that causes assertions to pass when they should fail. - Understand truthy and falsy values. Six values are falsy:
false,0,"",null,undefined, andNaN. Everything else is truthy. - Practice with real exercises. Run the four exercises in this article in your browser console or Node.js. Modify them until the concepts feel automatic.
FAQ
Do I need to learn JavaScript before TypeScript?
Yes. TypeScript is a superset of JavaScript. Every valid JavaScript file is valid TypeScript. The types and interfaces in TypeScript build on top of JavaScript fundamentals. Days 1 through 5 of this series cover JavaScript. Days 6 through 9 add TypeScript on top of that foundation.
Is JavaScript harder than Java or Python?
JavaScript has fewer rules than Java but more quirks. The type coercion and loose equality behaviors catch beginners off guard. However, the syntax is lightweight, and you can see results immediately in the browser console. Most testers pick up functional JavaScript within two weeks of daily practice.
What is the best way to practice JavaScript for automation?
Write small scripts that solve real problems: parse test data, build dynamic URLs, calculate pass rates, and manipulate JSON API responses. Do not just read tutorials. Write code, break it, fix it, and repeat. The exercises in this article are a starting point. Expand them with your own test scenarios.
When does Playwright enter this series?
Day 10. Days 1 through 9 build your JavaScript and TypeScript foundation. Day 10 installs Playwright and writes your first test. By Day 21, you will have a production-grade Playwright framework written in TypeScript.
Can I skip to Day 10 if I already know JavaScript?
You can, but I recommend skimming Days 1 through 9 anyway. I cover JavaScript from the perspective of test automation, not general web development. The variable scoping rules, truthy/falsy behaviors, and object reference patterns directly impact how you structure Playwright test data and assertions.
This is Day 1 of the 21-Day Playwright + TypeScript Tutorial Series. Day 2 covers Functions and Scope for Test Automation. Follow the full series at ScrollTest.com or subscribe to The Testing Academy YouTube channel for video walkthroughs of each concept.
