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.

javascriptjavascript
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

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

javascriptjavascript
let payload = { status: "ok", code: 200 };
payload["status"] = "error";
payload.code = 500;
console.log(payload);

Building an object step by step:

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

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

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

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

javascriptjavascript
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

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

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

javascriptjavascript
console.log(typeof {}); // "object"
console.log(typeof []); // "object"

A single element’s type is whatever that element is:

javascriptjavascript
let nums = ["10", "20", "30"];
console.log(typeof nums[0]); // "string"

Reliable array check:

javascriptjavascript
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

NeedUse
Fixed identifier keyobj.port
Dynamic / odd key nameobj[prop], obj["x-y"]
List own keysObject.keys(obj)
Is it an array?Array.isArray(x)
Variable as literal key{ [name]: value }

Suggested exercises

  1. Build let key = "password"; and an object that stores "secret" under that dynamic key using a literal with [key].
  2. Given let api = { data: [], error: null };, explain why typeof api.data is "object" and how you would assert it is an array in a test.
  3. Fix: a colleague wrote console.log(response[fieldName]) vs console.log(response.fieldName) — when does each return what you expect?
  4. Use Object.keys on { 2: "b", 1: "a", foo: "c" } in Node/console and observe key order (integer keys sorted, then foo).

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.