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

javascriptjavascript
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).

ExpressionResultWhy
"200" === 200falseDifferent types: string vs number
"200" == 200trueString coerced to number 200
0 === falsefalsenumber vs boolean
0 == falsetruefalse becomes 0
"" === falsefalsestring vs boolean
"" == falsetrueBoth sides end up comparable as falsy-like values in the algorithm
null === undefinedfalseDifferent types
null == undefinedtrueSpec 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

javascriptjavascript
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

javascriptjavascript
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:

javascriptjavascript
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
}
javascriptjavascript
let retriesLeft = 2;
if (retriesLeft) {
  console.log("can retry");
}

if (Boolean(retriesLeft) === true) {
  console.log("explicit boolean");
}
javascriptjavascript
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.

javascriptjavascript
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

javascriptjavascript
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:

javascriptjavascript
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).

javascriptjavascript
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

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

javascriptjavascript
let outcome = 1;
if (true) {
  outcome = 2;
}
console.log(outcome); // 2

11. Coercion traps in conditions

javascriptjavascript
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

TopicRecommendation in tests
EqualityPrefer === / !==
if bodyAlways { ... }
&& / `
else ifRange checks (e.g. HTTP 2xx); always handle else
TernaryShort assignments only; avoid deep nesting

Suggested exercises

  1. Create one variable statusCode. Write an if statement that treats any HTTP status from 200 to 299 as a successful response.
  2. Explain why the condition if (!items.length) can be used to check whether an array is empty.
  3. 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".