Lesson 4 of 14
Lesson 04 — Conditionals for QA automation
Title: if / else, Comparisons, Logical Operators, and Strict Equality
Description: Conditional logic controls test flow and branching. It is used to skip tests in certain environments, separate checks based on HTTP status codes, and validate optional fields. This lesson covers comparison operators, loose vs strict equality (== vs ===), logical operators (&&, ||), truthy and falsy values, else if, switch, and the ternary operator. It also highlights common mistakes like missing braces and unexpected type conversion.
**Why it matters for QA:**Many test issues come from type coercion with == or from if statements without braces, where only one line is controlled by the condition. In automation, it is important to use strict and explicit boolean logic to avoid unstable or incorrect test behavior.
1. Basic comparisons
let failedCount = 2;
if (failedCount > 0) {
console.log("has failures");
} else {
console.log("all green");
}
let exitCode = 0;
if (exitCode >= 0) {
console.log("non-negative exit");
}
if (exitCode === 0) {
console.log("exactly zero");
}
if (exitCode !== 1) {
console.log("not exit 1");
}
2. Loose vs Strict Comparison
Strict equality (===, !==): operands are compared without type conversion. If the types differ, the result is false. When types match, values are compared by the spec’s rules (for example, NaN === NaN is still false).
Loose equality (==, !=): JavaScript applies the Abstract Equality Comparison algorithm. If types differ, operands are coerced (to primitives, then often to numbers) until both sides have a comparable type, then they are compared. That is why "200" == 200 is true (string becomes number) and null == undefined is true (a dedicated rule in the spec).
| Expression | Result | Why |
|---|---|---|
"200" === 200 | false | Different types: string vs number |
"200" == 200 | true | String coerced to number 200 |
0 === false | false | number vs boolean |
0 == false | true | false becomes 0 |
"" === false | false | string vs boolean |
"" == false | true | Both sides end up comparable as falsy-like values in the algorithm |
null === undefined | false | Different types |
null == undefined | true | Spec rule for null / undefined pair |
In QA automation: treat == / != as a source of flaky or misleading assertions. Prefer === / !==, and when you need to compare across types (e.g. API returns a string status code), normalize explicitly with Number(...), String(...), or a small parser, then compare with ===.
The next section shows the same ideas in code, including string vs number status codes.
3. Strings and strict equality
let browser = "chromium";
if (browser === "chromium") {
console.log("supported");
}
// Loose equality coerces types — avoid in tests
if ("200" == 200) {
console.log("loose: equal"); // runs: string coerced to number
}
if ("200" === 200) {
console.log("strict: never");
} else {
console.log("strict: different types"); // runs
}
if ("200" != 200) {
console.log("loose !="); // does NOT run — coerced equal
}
if (200 !== 200) {
console.log("never");
} else {
console.log("strict !== same type and value check");
}
QA convention: default to === / !== so distinct primitive types (e.g. "0" vs 0) never compare equal.
4. Logical AND / OR
let httpStatus = 204;
if (httpStatus > 199 && httpStatus < 300) {
console.log("success class");
}
let shardA = 0;
let shardB = 7;
if (shardA > 0 || shardB > 0) {
console.log("at least one shard has work");
}
if (shardA >= 0 || shardB >= 0) {
console.log("typical: both could be valid counts");
}
5. Truthy and falsy
Falsy values in boolean context: false, 0, -0, 0n (BigInt zero), "", null, undefined, NaN. Everything else—including the string "0" and empty arrays []—is truthy.
Each falsy value skips the if body when used alone as the condition:
if (false) console.log("never");
if (0) console.log("never — zero is falsy");
if (-0) console.log("never — same as 0 in conditions");
if (0n) console.log("never — BigInt zero is falsy");
if ("") console.log("never — empty string is falsy");
if (null) console.log("never");
if (undefined) console.log("never");
if (NaN) console.log("never — NaN is falsy");
// Truthy: non-empty types or non-zero values
if ("0") console.log("runs — string with character '0' is truthy");
if ([]) console.log("runs — empty array is an object, truthy");
if ([].length) console.log("never — length 0 is falsy when coerced");
// Practical guard: only run when value is present
let maybeName = "";
if (maybeName) {
console.log("has name");
} else {
console.log("skip — empty string"); // runs
}
let count = 0;
if (count) {
console.log("has failures");
} else {
console.log("zero failures"); // runs — watch out in reports
}
let retriesLeft = 2;
if (retriesLeft) {
console.log("can retry");
}
if (Boolean(retriesLeft) === true) {
console.log("explicit boolean");
}
let gateOpen = true;
let tokenValid = true;
if (gateOpen && tokenValid) {
console.log("proceed");
}
if (gateOpen || tokenValid) {
console.log("either is enough for this example");
}
6. Pitfall: if without braces
Only the next statement is inside the if. The second console.log always runs.
let latencyMs = 40;
if (latencyMs > 0) console.log("latency:", latencyMs);
console.log("always printed"); // not part of the if!
Always use braces in team and test code to avoid bugs during edits.
7. else if ladder
let priority = 2;
if (priority === 1) {
console.log("P1");
} else if (priority === 2) {
console.log("P2");
} else if (priority === 3) {
console.log("P3");
} else {
console.log("unknown priority");
}
HTTP status ranges — same else if pattern; check from specific bands downward. Invalid or out-of-range codes hit else:
let statusCode = 255;
if (statusCode >= 100 && statusCode <= 199) {
console.log("Informational responses");
} else if (statusCode >= 200 && statusCode <= 299) {
console.log("Successful responses");
} else if (statusCode >= 300 && statusCode <= 399) {
console.log("Redirection messages");
} else if (statusCode >= 400 && statusCode <= 499) {
console.log("Client error responses");
} else if (statusCode >= 500 && statusCode <= 599) {
console.log("Server error responses");
} else {
console.log("ERROR status Code", statusCode); // 255 is not a standard HTTP code
}
QA note: 255 does not match any band above, so the final else runs. In tests, keep the else branch to catch typos, mocked values, or 0 when the API returns something unexpected.
8. switch
Useful for discrete values (status codes, browser names). Remember break (or intentional fall-through).
let suite = 2;
switch (suite) {
case 1:
console.log("smoke");
break;
case 2:
console.log("regression");
break;
case 3:
console.log("nightly");
break;
default:
console.log("custom");
break;
}
9. Ternary operator
let yearsOld = 17;
let canVote = yearsOld >= 18 ? true : false;
console.log(canVote); // false
// Often simplified (already boolean):
let isAdult = yearsOld >= 18;
10. Block scope
let inside a block does not leak out.
let outcome = 1;
if (true) {
outcome = 2;
}
console.log(outcome); // 2
11. Coercion traps in conditions
let partA = "4";
let partB = "2";
if (partA + partB === 42) {
console.log("never — left side is string '42'");
} else {
console.log("string concat is not number 42");
}
if (Number(partA + partB) === 42) {
console.log("numeric compare after parse");
}
let ticketId = 456;
// First character of string is always a string — compare to "4", not 4
if (String(ticketId)[0] === "4") {
console.log("starts with digit four");
}
Official docs
Quick recap
| Topic | Recommendation in tests |
|---|---|
| Equality | Prefer === / !== |
if body | Always { ... } |
&& / ` | |
else if | Range checks (e.g. HTTP 2xx); always handle else |
| Ternary | Short assignments only; avoid deep nesting |
Suggested exercises
- Create one variable
statusCode. Write anifstatement that treats any HTTP status from200to299as a successful response. - Explain why the condition
if (!items.length)can be used to check whether anarrayis empty. - Replace this logic with a switch statement:
"dev"→ log"Development""staging"→ log"Testing""prod"→ log"Production"Use the variable env.
Homework
About 10–15 minutes total. Click a task for the prompt.
Task 1: Status 200–299
let status = 204. With if { }, log "ok" only when status is from 200 to 299.
Task 2: Empty array
Use if (!items.length) with items = [], then with items = [1]. Log when the condition passes. One line: why does !items.length mean “empty”?
Task 3: Switch on env
let env = "staging". switch: different log per "dev", "staging", "prod", plus default.
Task 4: === vs ==
Log "200" === 200 and "200" == 200. One sentence: === or == in tests, and why?
Task 5: Ternary grade
let score = 85. Set grade with ? : ("pass" if score >= 70, else "fail"). Log grade.
Task 6: AND two flags
let loggedIn = true, let hasLicense = false. One if with && and braces: both true → "run paid tests", else "skip".
Task 7: OR two codes
let code = 204. One if with ||: 200 or 204 → "acceptable", else "other".
Task 8: !hasError
hasError = false: if (!hasError) → "continue", else → "stop". Run with false, then with true.
Task 9: Role + active
let role = "qa", let isActive = true. One if (use ()): ("qa" or "admin") and active → log "can run tests".
Task 10: Retry?
let status = 401, let isRetryable = false. Log "retry" if status >= 500 or isRetryable; else "do not retry".