DEV Community

Cover image for πŸ—‚οΈ How to Use Arrays and Objects in TypeScript for Powerful QA Automation Scripts
idavidov13
idavidov13

Posted on • Edited on • Originally published at idavidov.eu

πŸ—‚οΈ How to Use Arrays and Objects in TypeScript for Powerful QA Automation Scripts

In our last discussion, we learned how TypeScript acts as a safety net, catching errors with basic types like string and number. Now, it's time to upgrade your skills.

Think of basic types as individual toolsβ€”a screwdriver, a wrench. They're useful, but you can't build anything complex with them scattered around. This article is about organizing your toolbox. We'll explore how to group your data into collections using Arrays and Objects, allowing you to write cleaner, more powerful, and more realistic automation scripts.

Toolbox


βœ… Prerequisites

This article builds directly on the concepts from our first one. To get the most out of it, you should have a basic understanding of TypeScript's core types:

  • string
  • number
  • boolean

If you're comfortable with those, you're ready to start organizing!


⛓️ Working with Arrays: Your First Collection

Tests rarely deal with just one of anything. You're often working with a list of items: navigation links, search results, or products in a cart. An Array is a list of items of the same type.

Imagine you're testing a dropdown menu that contains a list of cryptocurrencies. In TypeScript, you would define this as an array of strings.

// An array of strings, denoted by string[]
const cryptoOptions: string[] = [
  'Bitcoin',
  'Litecoin',
  'Ethereum',
  'Cardano',
  'Solana'
];
Enter fullscreen mode Exit fullscreen mode

The [] after string tells TypeScript: "This is not just one string-it's a list of them." This ensures you can only add strings to this list, preventing bugs where a number or boolean might accidentally slip into your data.


🍱 Working with Tuples: Fixed and Focused

What if you have a list where the order and type of each item are fixed? For that, we have Tuples. A tuple is a fixed-length array where the type of each element is known.

A perfect QA example is user credentials, where you always have a username followed by a password.

// A tuple with a specific structure: [string, string]
type UserCredentials = [string, string];

const adminCredentials: UserCredentials = ['adminUser', 'P@ssw0rd123!'];

// This would cause an error! The structure is wrong.
// const invalidCredentials: UserCredentials = ['tester', 12345];
Enter fullscreen mode Exit fullscreen mode

Tuples are great for ensuring data pairs or small, fixed groups maintain their structure throughout your tests.


πŸ› οΈ Practical Power: Array Methods in Action

Let's get practical. Knowing how to define an array is one thing--manipulating it is where the real power lies. We'll use our crypto dropdown example with three essential methods: map, forEach, and filter.

Imagine you've used Playwright to locate all the options in the dropdown. You'd have an array of ElementHandles.

// This is a hypothetical Playwright locator
const allOptions = await page.$$('option.crypto-item');
Enter fullscreen mode Exit fullscreen mode

Now, let's see what we can do with allOptions.

.map() - Transforming Data

The .map() method creates a new array by transforming every element from an original array. We have element handles, but what we really want are the currency names (their text). .map() is perfect for this.

// The .map() method returns a new array of strings
const cryptoNames: string[] = await Promise.all(
    allOptions.map(option => option.textContent())
);

// cryptoNames is now ['Bitcoin', 'Litecoin', 'Ethereum', 'Cardano', 'Solana']
Enter fullscreen mode Exit fullscreen mode

Use Case: .map() is your go-to when you need to extract specific data from a list of elements, like getting all the href attributes from a list of links or all the prices from a list of products.

.forEach() - Performing Actions

The .forEach() method executes an action for each item in an array. It doesn't create a new array--it just loops through the existing one. It's best for when you need to "do something" with each item, like logging it or interacting with it.

// .forEach() performs an action for each item
cryptoNames.forEach(name => {
  console.log(`Verifying currency: ${name}`);
});
Enter fullscreen mode Exit fullscreen mode

Use Case: Use .forEach() to click a series of buttons, fill out multiple input fields in a sequence, or simply log data for debugging purposes.

.filter() - Finding What You Need

The .filter() method creates a new array containing only the elements that pass a specific condition. Need to find all enabled buttons or products that are on sale? filter is the tool for the job.

Let's find all the cryptocurrencies in our list that contain the letter "o".

// .filter() returns a new array with only the matching items
const o_cryptos: string[] = cryptoNames.filter(name => name.includes('o'));

// o_cryptos is now ['Bitcoin', 'Litecoin', 'Cardano', 'Solana']
Enter fullscreen mode Exit fullscreen mode

Use Case: .filter() is essential for assertions. You can filter for visible elements before asserting their count, or filter a list of products to ensure a specific item is present.

infographic


πŸ—‚οΈ Structuring Complex Data with Objects

Arrays are great for lists, but what about modeling a single, complex piece of data, like a user profile or an API request body? This is where Objects shine. Objects allow you to group related data under a single variable, using key-value pairs.

In TypeScript, we define the "shape" of an object using an interface or a type.

interface vs. type
Both can define object shapes. A common convention is to use interface for object models because they can be easily extended. Think of an AdminProfile extending a basic UserProfile. type is more versatile and can define unions or other complex combinations. For structuring data objects, interface is often a cleaner choice.

Let's define a UserProfile with an interface.

interface UserProfile {
  id: number;
  username: string;
  email: string;
  isProMember: boolean;
  lastLogin: Date;
}
Enter fullscreen mode Exit fullscreen mode

Now, we can create a user object that must conform to this shape. TypeScript will immediately flag any missing or mistyped properties.

// Create a user object that matches the interface
const testUser: UserProfile = {
  id: 101,
  username: 'qa_tester',
  email: '[email protected]',
  isProMember: false,
  lastLogin: new Date()
};

// Now, pass this neatly organized object into your test functions
async function login(user: UserProfile): Promise<void> {
  await page.getByRole('textbox', { name: 'Username' }).fill(user.username);
  await page.getByRole('textbox', { name: 'Email' }).fill(user.email);
  await page.getByRole('button', { name: 'Login' }).click();
}

// Call the function with our structured data
await login(testUser);
Enter fullscreen mode Exit fullscreen mode

By using an object, your code becomes incredibly clean and scalable. Instead of passing multiple separate arguments to your login function, you pass just one.

Clean Object


πŸš€ Your Next Step

You've moved from handling single data points to skillfully managing collections and complex structures. Mastering Arrays and Objects is the difference between writing fragile tests and building a robust, maintainable automation framework. The main takeaway is this: structure your test data just like the application does.

Your next challenge is to put this into practice.

Find a test in your project that uses multiple, related variables. Refactor it to use a single Object with an interface.

Instead of this:

const username = 'test';
const email = '[email protected]';
const id = 123;
Enter fullscreen mode Exit fullscreen mode

Create an object. You'll immediately see how much cleaner and more professional your code becomes.

"Efficiency is doing things right--effectiveness is doing the right things." - Peter Drucker


πŸ™πŸ» Thank you for reading! Building robust, scalable automation frameworks is a journey best taken together. If you found this article helpful, consider joining a growing community of QA professionals πŸš€ who are passionate about mastering modern testing.

Join the community and get the latest articles and tips by signing up for the newsletter.

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.