Practise JavaScript objects with 32 programs, each with a full solution, the exact output and a short explanation. The programs start with the basics, move to intermediate problems and finish with real-world scenarios from test automation, such as test data, prices, form inputs and test results. They are written for testers learning JavaScript for Playwright.
How to practise: read the task, write your own solution first, then open Show solution to compare. Run any program with node program.js.
New to this topic? Read the JavaScript Objects lesson first.
- Beginner programs (1–10)
- Intermediate programs (11–20)
- Real-world QA scenario programs (21–32)
JavaScript Beginner Programs
1. Create and read an objectBeginner
Create a user object with name, role and active, then print each property.
Show solution
const user = { name: 'Asha', role: 'tester', active: true };
console.log(user.name);
console.log(user['role']);
console.log(user.active);
Output
Asha
tester
true
Explanation: dot notation is the usual way to read properties. Bracket notation also works, and is needed for dynamic keys.
2. Add, update and delete propertiesBeginner
Add an email, change the role and delete active from a user object.
Show solution
const user = { name: 'Asha', role: 'tester', active: true };
user.email = 'asha@test.com';
user.role = 'lead';
delete user.active;
console.log(user);
Output
{ name: 'Asha', role: 'lead', email: 'asha@test.com' }
Explanation: objects are mutable, so properties can be added, changed or removed after creation.
3. Dynamic property namesBeginner
Read a property whose name is stored in a variable.
Show solution
const settings = { browser: 'chromium', retries: 2 };
const key = 'retries';
console.log(settings[key]);
console.log(settings.key);
Output
2
undefined
Explanation: settings[key] uses the variable’s value. settings.key looks for a property literally called key.
4. Check whether a property existsBeginner
Check for retries and proxy with in and Object.hasOwn.
Show solution
const config = { retries: 0 };
console.log('retries' in config);
console.log(Object.hasOwn(config, 'proxy'));
console.log(config.retries !== undefined);
Output
true
false
true
Explanation: in and Object.hasOwn check existence even when the value is falsy, such as 0.
5. Keys, values and entriesBeginner
Print the keys, values and entries of a test-case object.
Show solution
const testCase = { id: 'TC-1', priority: 'high', automated: true };
console.log(Object.keys(testCase));
console.log(Object.values(testCase));
console.log(Object.entries(testCase).length);
Output
[ 'id', 'priority', 'automated' ]
[ 'TC-1', 'high', true ]
3
Explanation: these methods turn an object into arrays that you can loop over or transform.
6. Loop over an objectBeginner
Print each property of a config object as key: value.
Show solution
const config = { browser: 'firefox', headless: false, workers: 2 };
for (const [key, value] of Object.entries(config)) {
console.log(`${key}: ${value}`);
}
Output
browser: firefox
headless: false
workers: 2
Explanation: Object.entries with for...of and destructuring gives both key and value on each pass.
7. Object with a methodBeginner
Create a calculator object with add and subtract methods.
Show solution
const calculator = {
add(a, b) { return a + b; },
subtract(a, b) { return a - b; }
};
console.log(calculator.add(10, 5));
console.log(calculator.subtract(10, 5));
Output
15
5
Explanation: functions stored on an object are called methods.
8. Using this in a methodBeginner
Create a user object whose describe method uses this.
Show solution
const user = {
name: 'Ravi',
role: 'admin',
describe() {
return `${this.name} is an ${this.role}`;
}
};
console.log(user.describe());
Output
Ravi is an admin
Explanation: inside a regular method, this refers to the object the method was called on.
9. Shorthand propertiesBeginner
Build an object from variables using shorthand property names.
Show solution
const browser = 'webkit';
const viewport = '1280x720';
const project = { browser, viewport };
console.log(project);
Output
{ browser: 'webkit', viewport: '1280x720' }
Explanation: { browser } is shorthand for { browser: browser } when the variable and property share a name.
10. Nested objectsBeginner
Read nested properties from an order object.
Show solution
const order = { id: 501, customer: { name: 'Meera', address: { city: 'Pune' } } };
console.log(order.customer.name);
console.log(order.customer.address.city);
Output
Meera
Pune
Explanation: chain property access to reach nested values.
JavaScript Intermediate Programs
11. Object destructuring with defaultsIntermediate
Destructure an options object with defaults and a renamed property.
Show solution
const options = { timeout: 5000, headless: false };
const { timeout, headless = true, retries = 1, timeout: waitMs } = options;
console.log(timeout, headless, retries, waitMs);
Output
5000 false 1 5000
Explanation: defaults apply only when a property is undefined. timeout: waitMs copies the same value into a new name.
12. Merge objects with spreadIntermediate
Merge default settings with overrides, keeping the overrides.
Show solution
const defaults = { browser: 'chromium', retries: 0, video: 'off' };
const overrides = { retries: 2, video: 'on' };
const merged = { ...defaults, ...overrides };
console.log(merged);
Output
{ browser: 'chromium', retries: 2, video: 'on' }
Explanation: later spreads overwrite earlier properties with the same name.
13. Shallow vs deep copyIntermediate
Show that a spread copy shares nested objects, and fix it with structuredClone.
Show solution
const original = { name: 'cart', items: ['pen'] };
const shallow = { ...original };
const deep = structuredClone(original);
shallow.items.push('book');
deep.items.push('bag');
console.log(original.items);
console.log(deep.items);
Output
[ 'pen', 'book' ]
[ 'pen', 'bag' ]
Explanation: spread copies only the top level. structuredClone copies nested data too.
14. Freeze and sealIntermediate
Compare Object.freeze and Object.seal.
Show solution
const frozen = Object.freeze({ env: 'qa' });
const sealed = Object.seal({ env: 'qa' });
frozen.env = 'prod';
sealed.env = 'prod';
sealed.extra = true;
console.log(frozen.env, sealed.env, 'extra' in sealed);
Output
qa prod false
Explanation: frozen objects cannot change at all. Sealed objects allow value changes but no new or deleted properties.
15. Count propertiesIntermediate
Count how many properties an object has.
Show solution
const response = { id: 1, name: 'Item', price: 99, inStock: true };
console.log(Object.keys(response).length);
Output
4
Explanation: objects have no length, so count their keys instead.
16. Transform an objectIntermediate
Apply a 10% discount to every price in a price list object.
Show solution
const prices = { pen: 20, book: 350, bag: 900 };
const discounted = Object.fromEntries(
Object.entries(prices).map(([item, price]) => [item, price * 0.9])
);
console.log(discounted);
Output
{ pen: 18, book: 315, bag: 810 }
Explanation: convert to entries, map them, and convert back with Object.fromEntries.
17. Filter object propertiesIntermediate
Keep only the settings whose values are not null.
Show solution
const settings = { browser: 'chromium', proxy: null, retries: 2, baseURL: null };
const cleaned = Object.fromEntries(Object.entries(settings).filter(([, value]) => value !== null));
console.log(cleaned);
Output
{ browser: 'chromium', retries: 2 }
Explanation: filtering entries is the object equivalent of array.filter.
18. Compare two objectsIntermediate
Write a function that checks whether two flat objects have the same keys and values.
Show solution
function shallowEqual(a, b) {
const keysA = Object.keys(a);
const keysB = Object.keys(b);
if (keysA.length !== keysB.length) return false;
return keysA.every(key => a[key] === b[key]);
}
console.log(shallowEqual({ x: 1, y: 2 }, { y: 2, x: 1 }));
console.log(shallowEqual({ x: 1 }, { x: '1' }));
Output
true
false
Explanation: === compares object references, so content equality needs a key-by-key check.
19. Getters and settersIntermediate
Create an object with a fullName getter and setter.
Show solution
const person = {
first: 'Asha',
last: 'Sharma',
get fullName() { return `${this.first} ${this.last}`; },
set fullName(value) { [this.first, this.last] = value.split(' '); }
};
console.log(person.fullName);
person.fullName = 'Neha Verma';
console.log(person.first, person.last);
Output
Asha Sharma
Neha Verma
Explanation: getters and setters run code when a property is read or written, while looking like normal properties.
20. Optional chaining on missing dataIntermediate
Read a deeply nested value that may not exist.
Show solution
const responses = [
{ data: { user: { profile: { country: 'IN' } } } },
{ data: { user: null } },
{}
];
for (const r of responses) console.log(r.data?.user?.profile?.country ?? 'unknown');
Output
IN
unknown
unknown
Explanation: ?. stops at the first null or undefined, so missing data never throws.
JavaScript Real-World QA Scenario Programs
21. Validate an API responseReal-world
Check that an API user object has all required fields with the right types.
Show solution
const required = { id: 'number', email: 'string', active: 'boolean' };
const user = { id: 12, email: 'qa@test.com', active: 'true' };
const problems = Object.entries(required)
.filter(([field, type]) => typeof user[field] !== type)
.map(([field, type]) => `${field} should be ${type}`);
console.log(problems.length ? problems.join('; ') : 'Response is valid');
Output
active should be boolean
Explanation: a small schema object lets one loop check every field, a lightweight contract test.
22. Build test data with defaultsReal-world
Write buildUser(overrides) that returns a default user merged with overrides.
Show solution
function buildUser(overrides = {}) {
return { name: 'QA User', email: 'qa.user@example.com', role: 'viewer', active: true, ...overrides };
}
console.log(buildUser().role);
const admin = buildUser({ role: 'admin', name: 'Admin' });
console.log(admin.name, admin.role, admin.active);
Output
viewer
Admin admin true
Explanation: a data builder gives sensible defaults, and each test overrides only what it needs.
23. Count tests by statusReal-world
Use an object to count statuses in a results array.
Show solution
const statuses = ['passed', 'failed', 'passed', 'flaky', 'passed', 'failed'];
const counts = {};
for (const s of statuses) counts[s] = (counts[s] ?? 0) + 1;
console.log(counts);
Output
{ passed: 3, failed: 2, flaky: 1 }
Explanation: object keys work as counters, a pattern used in every test report.
24. Environment config lookupReal-world
Store base URLs per environment in an object and pick one by name.
Show solution
const environments = {
qa: { baseURL: 'https://qa.shop.test', retries: 1 },
staging: { baseURL: 'https://staging.shop.test', retries: 2 }
};
const envName = 'staging';
const env = environments[envName] ?? environments.qa;
console.log(env.baseURL, env.retries);
Output
https://staging.shop.test 2
Explanation: an object lookup replaces a chain of if statements and falls back to a default.
25. Map API fields to UI labelsReal-world
Rename snake_case API keys to readable labels.
Show solution
const apiUser = { first_name: 'Ravi', last_name: 'Kumar', phone_no: '9876543210' };
const labels = { first_name: 'First name', last_name: 'Last name', phone_no: 'Phone' };
for (const [key, value] of Object.entries(apiUser)) {
console.log(`${labels[key]}: ${value}`);
}
Output
First name: Ravi
Last name: Kumar
Phone: 9876543210
Explanation: a mapping object keeps label translations in one place.
26. Diff expected vs actual objectsReal-world
Report which fields differ between expected and actual user details.
Show solution
const expected = { name: 'Asha', city: 'Delhi', plan: 'pro' };
const actual = { name: 'Asha', city: 'Mumbai', plan: 'free' };
for (const key of Object.keys(expected)) {
if (expected[key] !== actual[key]) {
console.log(`${key}: expected '${expected[key]}', got '${actual[key]}'`);
}
}
Output
city: expected 'Delhi', got 'Mumbai'
plan: expected 'pro', got 'free'
Explanation: field-level differences make failed assertions much easier to understand than a plain false.
27. Group products by categoryReal-world
Group product names under their category.
Show solution
const products = [
{ name: 'Mouse', category: 'electronics' },
{ name: 'Pen', category: 'stationery' },
{ name: 'Keyboard', category: 'electronics' }
];
const groups = {};
for (const p of products) (groups[p.category] ??= []).push(p.name);
console.log(groups);
Output
{ electronics: [ 'Mouse', 'Keyboard' ], stationery: [ 'Pen' ] }
Explanation: ??= creates the array the first time a category appears, then each product is pushed into it.
28. Read headers case-insensitivelyReal-world
Find the Content-Type header regardless of letter case.
Show solution
const headers = { 'content-type': 'application/json', 'X-Request-Id': 'abc-1' };
function getHeader(all, name) {
const key = Object.keys(all).find(k => k.toLowerCase() === name.toLowerCase());
return key ? all[key] : undefined;
}
console.log(getHeader(headers, 'Content-Type'));
console.log(getHeader(headers, 'x-request-id'));
Output
application/json
abc-1
Explanation: HTTP header names are case-insensitive, so compare them in lower case.
29. Remove sensitive fields before loggingReal-world
Copy a user object without its password and token before printing it.
Show solution
const user = { id: 3, email: 'qa@test.com', password: 'Secret!23', token: 'abc.def' };
const { password, token, ...safeUser } = user;
console.log(safeUser);
Output
{ id: 3, email: 'qa@test.com' }
Explanation: rest destructuring collects every property except the named ones, a quick way to strip secrets.
30. Convert form data to a query stringReal-world
Turn a filter object into a URL query string, skipping empty values.
Show solution
const filters = { q: 'laptop', minPrice: 30000, brand: '', sort: 'price_asc' };
const params = new URLSearchParams(
Object.entries(filters).filter(([, v]) => v !== '').map(([k, v]) => [k, String(v)])
);
console.log(params.toString());
Output
q=laptop&minPrice=30000&sort=price_asc
Explanation: filter out empty values, then URLSearchParams handles encoding.
31. Track a page object’s stateReal-world
Create a cart object with methods that add items and calculate the total.
Show solution
const cart = {
items: [],
add(name, price, qty = 1) { this.items.push({ name, price, qty }); return this; },
total() { return this.items.reduce((sum, i) => sum + i.price * i.qty, 0); }
};
cart.add('Mouse', 599).add('Cable', 199, 2);
console.log(cart.items.length, 'items, total', cart.total());
Output
2 items, total 997
Explanation: methods share state through this, and returning this allows chaining calls.
32. Convert an array of rows into objectsReal-world
Convert CSV rows into objects using the header row as keys.
Show solution
const rows = [['id', 'user', 'status'], ['1', 'asha', 'active'], ['2', 'ravi', 'locked']];
const [header, ...data] = rows;
const records = data.map(row => Object.fromEntries(header.map((h, i) => [h, row[i]])));
console.log(records[1]);
console.log(records.length);
Output
{ id: '2', user: 'ravi', status: 'locked' }
2
Explanation: pairing each header with the value at the same index builds one object per row.