Day 27: Testing & Interview Preparation
✅ Today you will learn
- Why automated tests save time (and jobs)
- Unit tests with node:test and assert
- Test edge cases and fix a bug test-first
- Jest/Vitest for units, Playwright for the browser
- The top JavaScript interview questions and coding tasks
🏢 In the real world
During the Diwali rush a developer changed the delivery-fee rule and accidentally made every order above ₹1,500 cost ₹60 extra. Nobody noticed for two days. A 10-line test would have caught it in one second before the change went live. Companies now expect freshers to write basic tests – and to answer the classic JavaScript questions confidently.
Manual checking vs automated tests
Manual testing
- Click through the site after every change
- Slow, boring, easy to skip
- Edge cases forgotten
- Bugs reach customers
Automated tests
- Code that checks code
- Runs hundreds of checks in seconds
- Edge cases written once, checked forever
- Bugs caught before deploy (CI)
Your first tests with node:test
const { test } = require('node:test');
const assert = require('node:assert/strict');
// --- code under test ---
const FREE_FROM = 1500, FEE = 60;
function deliveryFee(subtotal) {
if (subtotal <= 0) return 0;
return subtotal >= FREE_FROM ? 0 : FEE;
}
function orderTotal(items, coupon) {
const subtotal = items.reduce((s, i) => s + i.price * i.qty, 0);
const discounted = coupon === 'SWEET10' ? Math.round(subtotal * 0.9) : subtotal;
return discounted + deliveryFee(discounted);
}
// --- tests ---
test('delivery is free from ₹1,500', () => {
assert.equal(deliveryFee(1500), 0);
assert.equal(deliveryFee(2400), 0);
});
test('₹60 delivery below ₹1,500', () => {
assert.equal(deliveryFee(1499), 60);
});
test('empty cart has no delivery fee', () => {
assert.equal(deliveryFee(0), 0);
});
test('coupon is applied before the delivery rule', () => {
// 1600 − 10% = 1440 → below 1500 → +60
assert.equal(orderTotal([{ price: 800, qty: 2 }], 'SWEET10'), 1500);
});| Assertion | Checks |
|---|---|
| assert.equal(a, b) | a === b (strict module) |
| assert.deepEqual(obj1, obj2) | Same structure and values |
| assert.ok(value) | Truthy |
| assert.throws(() => fn(), /message/) | Throws the expected error |
| await assert.rejects(promise) | An async function fails as expected |
Pure functions with money, dates and rules: totals, discounts, GST, validation. Test the boundaries – exactly 1500, 1499, 0, negative, empty, very large – that is where bugs live.
Fixing a bug test-first
const { test } = require('node:test');
const assert = require('node:assert/strict');
// Bug report: "Mobile +91 98290 12345 is rejected on the order form"
function isValidMobile(input) {
const digits = input.replace(/\D/g, '');
const ten = digits.length === 12 && digits.startsWith('91') ? digits.slice(2)
: digits.length === 11 && digits.startsWith('0') ? digits.slice(1)
: digits; // ← the fix: accept 91/0 prefixes
return /^[6-9]\d{9}$/.test(ten);
}
test('accepts common Indian formats (the bug report)', () => {
for (const ok of ['9829012345', '+91 98290 12345', '098290-12345', '91-9829012345']) {
assert.ok(isValidMobile(ok), ok);
}
});
test('still rejects invalid numbers', () => {
for (const bad of ['5829012345', '98290', '+1 202 555 0101', '']) {
assert.equal(isValidMobile(bad), false, bad);
}
});- 1Write a test that reproduces the bug – it fails (red)
- 2Make the smallest change that fixes it – test passes (green)
- 3Clean up the code; tests still pass (refactor)
- 4Keep the test forever so the bug can never come back
Testing tools you will meet at work
| Tool | Type | Example use |
|---|---|---|
| node:test | Built-in unit tests | Utilities, Node APIs |
| Vitest / Jest | Unit + component tests | Functions, React components (with Testing Library) |
| Playwright / Cypress | End-to-end in real browsers | “Add to cart → checkout → see order number” |
| Lighthouse CI | Performance & accessibility budgets | Fail the build if LCP gets worse |
| GitHub Actions | Continuous integration | Run all tests on every push / pull request |
import { test, expect } from '@playwright/test';
test('customer can order kaju katli', async ({ page }) => {
await page.goto('http://localhost:5173/');
await page.getByRole('button', { name: /add kaju katli/i }).click();
await page.getByRole('link', { name: /cart/i }).click();
await page.getByLabel('Name').fill('Aarti Gupta');
await page.getByLabel('Mobile').fill('9829012345');
await page.getByRole('button', { name: 'Place order' }).click();
await expect(page.getByText(/order SS\d+/i)).toBeVisible();
});Top interview questions – quick answers
Concept questions (tap to reveal)
Q1. What are the data types in JavaScript?
Primitives: string, number, bigint, boolean, undefined, null, symbol. Everything else is an object (arrays, functions, dates…).
Q2. Difference between == and ===?
=== compares value and type; == converts types first. Use ===.
Q3. What is hoisting?
Declarations are registered before code runs: functions fully, var as undefined, let/const in the temporal dead zone.
Q4. What is a closure?
A function that remembers variables from where it was created – used for private state, factories, debounce.
Q5. Explain the event loop.
One call stack; async callbacks wait in queues; microtasks (promises) run before tasks (timers, events) when the stack is empty.
Q6. map vs forEach?
map returns a new transformed array; forEach returns undefined and is for side effects.
Q7. What is event delegation?
One listener on a parent handles events from many (also future) children via bubbling and e.target.closest().
Q8. How does this work?
Determined by the call: obj.method() → obj; plain call → undefined (strict); new → instance; bind/call/apply → given value; arrows use outer this.
Q9. Promise.all vs allSettled?
all fails fast on the first rejection; allSettled waits for all and reports each result.
Q10. localStorage vs cookies?
localStorage: ~5 MB, JS-readable, not sent to server. Cookies: small, sent with requests, can be HttpOnly – use for sessions.
Q11. Debounce vs throttle?
Debounce runs after calls stop (search box); throttle runs at most once per interval (scroll).
Q12. How do you prevent XSS?
Render user data as text, escape/sanitise HTML, validate URLs, avoid eval, use CSP and HttpOnly cookies.
Classic coding questions
// 1. Reverse words, 2. palindrome, 3. frequency count, 4. flatten, 5. remove duplicates, 6. chunk, 7. deep equal
const reverseWords = s => s.trim().split(/\s+/).reverse().join(' ');
const isPalindrome = s => { const c = s.toLowerCase().replace(/[^a-z0-9]/g, ''); return c === [...c].reverse().join(''); };
const frequency = arr => arr.reduce((m, x) => ((m[x] = (m[x] || 0) + 1), m), {});
const flatten = arr => arr.reduce((out, x) => out.concat(Array.isArray(x) ? flatten(x) : x), []);
const unique = arr => [...new Set(arr)];
const chunk = (arr, n) => Array.from({ length: Math.ceil(arr.length / n) }, (_, i) => arr.slice(i * n, i * n + n));
const deepEqual = (a, b) => {
if (a === b) return true;
if (typeof a !== 'object' || typeof b !== 'object' || !a || !b) return false;
const ka = Object.keys(a), kb = Object.keys(b);
return ka.length === kb.length && ka.every(k => deepEqual(a[k], b[k]));
};
console.log(reverseWords(' fresh ghewar daily '));
console.log(isPalindrome('Madam, in Eden I’m Adam'), isPalindrome('Rasmalai'));
console.log(frequency(['kaju', 'ladoo', 'kaju', 'ghewar', 'kaju']));
console.log(flatten([1, [2, [3, [4]], 5]]));
console.log(unique([302001, 302003, 302001, 302012]));
console.log(chunk(['A101', 'A102', 'A103', 'A104', 'A105'], 2));
console.log(deepEqual({ id: 1, tags: ['veg'] }, { id: 1, tags: ['veg'] }), deepEqual({ a: 1 }, { a: '1' }));| Prepare | How |
|---|---|
| Explain your projects | STAR format: situation, task, action, measurable result |
| Live coding | Think aloud, start simple, test edge cases, then optimise |
| Machine-coding round | Build a to-do / search with debounce / modal in 60–90 min – exactly what this course practised |
| Fundamentals | Closures, this, event loop, promises, array methods, DOM, CSS layout |
| Portfolio | Live links: HTML portfolio, CSS redesign, JS to-do, quiz and the shop project |
🏅 Job skills unlocked today
Practice tasks
🏋️ Practice – do it yourself
Show solution
const { test } = require('node:test');
const assert = require('node:assert/strict');
function grade(m) {
if (m < 0 || m > 100) throw new RangeError('marks must be 0–100');
if (m >= 90) return 'A+';
if (m >= 75) return 'A';
if (m >= 60) return 'B';
if (m >= 33) return 'C';
return 'Fail';
}
test('grade boundaries', () => {
assert.equal(grade(100), 'A+');
assert.equal(grade(90), 'A+');
assert.equal(grade(89), 'A');
assert.equal(grade(33), 'C');
assert.equal(grade(32), 'Fail');
assert.equal(grade(0), 'Fail');
});
test('invalid marks throw', () => {
assert.throws(() => grade(-5), RangeError);
});Show solution
function secondHighest(totals) {
const sorted = [...new Set(totals)].sort((a, b) => b - a);
return sorted.length > 1 ? sorted[1] : null;
}
console.log(secondHighest([1800, 650, 2400, 2400, 900]));
console.log(secondHighest([500, 500]));Quick quiz
🧠 Quick Quiz – test yourself
Interview corner
💼 Interview Corner
How do you decide what to test?
Test business-critical logic and boundaries first (money, validation, permissions), then main user flows end-to-end; aim for confidence, not 100% coverage; add a regression test for every fixed bug; keep tests fast and independent.
Tell me about a bug you fixed.
Use STAR: the symptom and impact, how you reproduced it, how you found the cause (DevTools, logs), the fix, the test you added and what you changed to prevent similar bugs.
Summary
- Automated tests catch bugs in seconds and protect against regressions.
- node:test + assert for units; test boundaries; fix bugs test-first.
- Vitest/Jest for units and components, Playwright for real user flows, CI to run them.
- Master the core questions and classic coding tasks; explain projects with STAR.
Day 28 – Mini project: a timed quiz app with scoring, review and high scores.
