Lesson 3 of 14
Lesson 03 — Objects for QA automation
Title: Key–Value Objects, Dot vs Bracket Notation, and Dynamic Keys
Description: Objects are used to model structured data like JSON request and response bodies, configuration maps, and test fixtures. This lesson covers how to create objects, read and update properties, use computed keys, and work with Object.keys, the in operator, and delete. It also explains the difference between objects and arrays.
Why it matters for QA: In tests, you often check nested data like response.body.user.email or fixture.env.baseURL. If you use obj.key when key is a variable, JavaScript will not read the expected property, which can cause hidden bugs in your tests.
How to practice: Execute snippets in DevTools console or Node.js REPL.
1. Empty object and numeric-looking keys
Keys in object literals are usually strings or identifiers. Numeric literals in { 1: "a" } are still string keys "1" when accessed normally.
let empty = {};
let expectedMessageByStatus = {
200: "OK",
404: "Not Found",
500: "Internal Server Error",
};
console.log(expectedMessageByStatus[200]); // "OK" — bracket with number coerces to string key
console.log(expectedMessageByStatus["200"]); // same
// SyntaxError: Unexpected number after property access
// console.log(expectedMessageByStatus.200);
2. Identifier keys and dot notation
let config = { host: "localhost", port: 3000, tls: false };
console.log(config.host);
console.log(config.port);
let expectedEmail = "qa@test.example";
let userFixture = {
email: expectedEmail,
role: "tester",
active: true,
};
console.log(userFixture.email);
3. Bracket vs dot for writing
Both forms can assign the same property if the key is a simple name.
let payload = { status: "ok", code: 200 };
payload["status"] = "error";
payload.code = 500;
console.log(payload);
Building an object step by step:
let headers = {};
headers["content-type"] = "application/json";
headers["x-request-id"] = "abc-123";
// Same keys with valid identifiers could use dot notation:
let meta = {};
meta.runId = 42;
meta.shard = 0;
console.log(headers, meta);
// SyntaxError: Unexpected token '-'
// headers.content-type = "text/plain";
4. Object.keys
Returns an array of own enumerable string keys (order: integer-like keys ascending, then others in insertion order—details matter for snapshots).
let user = { id: 1, role: "admin", active: true };
console.log(Object.keys(user)); // ["id", "role", "active"]
5. Dynamic key: bracket notation (correct)
When the property name lives in a variable, you must use brackets.
let row = { sku: "A1", qty: 5, price: 9.99 };
let field = "sku";
console.log(row[field]); // "A1"
6. Common mistake: obj.key when key is a variable
obj.key always reads the literal property named "key", not the value of the variable key.
let data = { a: 1, b: 2, key: "oops" };
let key = "b";
console.log(data[key]); // 2 — dynamic
console.log(data.key); // "oops" — literal property name "key", NOT variable key
7. Computed property names in literals
Use square brackets inside the literal when the key should be an expression.
let propName = "status";
let result = {
[propName]: "passed",
durationMs: 1200,
retries: 0,
};
console.log(result);
let field = "traceId";
let response = {
[field]: "abc-001",
code: 200,
body: "ok",
};
console.log(response.traceId);
8. Key existence with in
let env = { baseUrl: "https://api.example", timeout: 5000 };
console.log("baseUrl" in env); // true
console.log("headers" in env); // false
Note: in walks the prototype chain. For “own property only”, you will later use Object.hasOwn or Object.prototype.hasOwnProperty.call(obj, key).
9. delete on an object property
Removes an own property from the object.
let session = { token: "abc", expiresAt: 123, user: "qa" };
delete session.expiresAt;
console.log(session); // { token: "abc", user: "qa" }
In modern code, prefer creating a new object without the field when you want immutability (easier for tests and React state). delete is still useful in quick scripts and some legacy patterns.
10. typeof — objects vs arrays (the trap)
Both arrays and plain objects report "object" with typeof.
console.log(typeof {}); // "object"
console.log(typeof []); // "object"
A single element’s type is whatever that element is:
let nums = ["10", "20", "30"];
console.log(typeof nums[0]); // "string"
Reliable array check:
console.log(Array.isArray([])); // true
console.log(Array.isArray({})); // false
Use Array.isArray(x) before you call array methods on a value that might be an object (for example, parsed JSON from an API).
Official docs
Quick recap
| Need | Use |
|---|---|
| Fixed identifier key | obj.port |
| Dynamic / odd key name | obj[prop], obj["x-y"] |
| List own keys | Object.keys(obj) |
| Is it an array? | Array.isArray(x) |
| Variable as literal key | { [name]: value } |
Suggested exercises
- Build
let key = "password";and an object that stores"secret"under that dynamic key using a literal with[key]. - Given
let api = { data: [], error: null };, explain whytypeof api.datais"object"and how you would assert it is an array in a test. - Fix: a colleague wrote
console.log(response[fieldName])vsconsole.log(response.fieldName)— when does each return what you expect? - Use
Object.keyson{ 2: "b", 1: "a", foo: "c" }in Node/console and observe key order (integer keys sorted, thenfoo).
Homework
Short tasks (about 10–15 minutes). Click a task title to reveal the prompt.
Task 1: computed key in a literal
Create a constant field with the value "traceId".
Then create an object using a dynamic key (computed property name) so that the key comes from field and its value is "abc-001".
Task 2: array or object?
For let payload = { items: [] };, print typeof payload.items and Array.isArray(payload.items).
Then in one sentence, explain why typeof alone cannot confirm that the value is an array.
Task 3: bracket vs dot mistake
Given let data = { id: 1, name: "qa" }; and let key = "name", log data[key] and data.key.
Explain what the second expression returns and why.
Task 4: list keys
Log Object.keys for { z: 1, a: 2 } and observe the order (insertion order for non-integer keys).
Task 5: remove a property
Start with let session = { token: "x", ttl: 60 };, delete ttl, then log session.
Task 6: hyphenated header key
Create an empty object headers, then add a property using bracket notation where the key is "content-type" and the value is "application/json". Finally, log headers["content-type"] to show the value stored under that dynamic key.
Task 7: nested counts
Given let report = { api: { passed: 5, failed: 1 } };, log only the failed count using nested dot or bracket access.
Task 8: check property with in
Set let cfg = { timeout: 5000, retries: 2 }. Log "timeout" in cfg and "baseUrl" in cfg, then explain in one sentence what in checks.
Task 9: copy and extend object
Given let user = { name: "Ann", role: "qa" }, add user.active = true, then change role to "admin" and log the final object.
Task 10: check own key
Create let response = { code: 200, body: "ok", traceId: "t-1" }. Delete traceId, then log Object.keys(response) and the object.