EthicsComputerCourses
Join training +91 90532 10052

Test Data

Software Testing · Lesson 7 of 30 Beginner 6 min read Updated

A test is only as good as the data you feed it. The same login test can pass with "Priya" and fail with "D'Souza". Test data is every input value, account, file and database record you prepare so that tests can run and reveal problems.

In this lesson you will learn the kinds of test data you need, where test data comes from, how to generate repeatable synthetic data, why real customer data must be masked, and how a small list of tricky inputs finds bugs quickly.

Kinds of test data

KindPurposeExample for a signup form
ValidCheck the normal path worksmeera@example.com, age 24
InvalidCheck bad input is refusedmeera@, age -3
BoundaryCheck the exact edges of rulesage 17, 18, 60, 61 when the rule is 18 to 60
Empty and missingCheck required fieldsempty email, spaces only
Special charactersCheck text handlingD'Souza, Mohan-Lal, names in Hindi
Large volumeCheck limits and performance10,000 orders, a 300-character address
State dataPut the system in a known statea locked account, an expired coupon, an empty cart

You will learn how to choose valid, invalid and boundary values systematically in Equivalence Partitioning & Boundary Values.

Where test data comes from

  • Created by hand. Small, precise sets written into test cases. Best for specific checks.
  • Generated (synthetic). Created by a script or a library such as Faker. Best for volume and variety, and it contains no real person's details.
  • Copied from production. Realistic, but it contains real customers' personal data. It must be masked or anonymised before it reaches a test environment, and many teams avoid it completely.

Generating repeatable synthetic data

Random data is useful because it finds cases you did not think of. But if a test fails with random data, you must be able to reproduce it. The trick is a seed: give the random number generator the same starting value and it produces the same "random" sequence every time.

PythonRun it yourself
import random

random.seed(42)  # same seed, same data on every run

FIRST = ["Aarav", "Priya", "Kabir", "Meera", "Rohan", "Ananya", "Farhan", "Isha"]
CITIES = ["Rohtak", "Jaipur", "Pune", "Kochi", "Lucknow"]

def make_customer(n):
    name = random.choice(FIRST)
    return {
        "id": f"CUST-{n:03d}",
        "name": name,
        "email": f"{name.lower()}{n}@example.com",
        "city": random.choice(CITIES),
        "wallet": random.randint(0, 5000),
    }

customers = [make_customer(n) for n in range(1, 6)]
for c in customers:
    print(c)
Output
{'id': 'CUST-001', 'name': 'Priya', 'email': 'priya1@example.com', 'city': 'Rohtak', 'wallet': 2253}
{'id': 'CUST-002', 'name': 'Meera', 'email': 'meera2@example.com', 'city': 'Jaipur', 'wallet': 1143}
{'id': 'CUST-003', 'name': 'Priya', 'email': 'priya3@example.com', 'city': 'Lucknow', 'wallet': 712}
{'id': 'CUST-004', 'name': 'Farhan', 'email': 'farhan4@example.com', 'city': 'Rohtak', 'wallet': 244}
{'id': 'CUST-005', 'name': 'Priya', 'email': 'priya5@example.com', 'city': 'Jaipur', 'wallet': 1905}

Run it twice: the output is identical. Remove the random.seed(42) line and the data changes on every run. Notice that each email includes the customer number, so the generated emails stay unique even when names repeat. The domain example.com is reserved for documentation and testing, so no real person receives mail there.

Masking personal data

If a team must use production-like data, data masking replaces sensitive values with safe ones while keeping the format, so the application still behaves normally. This example masks names, emails and phone numbers:

PythonRun it yourself
import re

def mask_email(email):
    user, domain = email.split("@")
    return user[0] + "***@" + domain

def mask_phone(phone):
    digits = re.sub(r"\D", "", phone)
    return "XXXXXX" + digits[-4:]

production_rows = [
    {"name": "Ananya Sharma", "email": "ananya.sharma@mailbox.in", "phone": "+91 98765 43210"},
    {"name": "Farhan Ali", "email": "farhan.ali@mailbox.in", "phone": "98111-22334"},
]

for i, row in enumerate(production_rows, start=1):
    safe = {
        "name": f"Customer {i}",
        "email": mask_email(row["email"]),
        "phone": mask_phone(row["phone"]),
    }
    print(safe)
Output
{'name': 'Customer 1', 'email': 'a***@mailbox.in', 'phone': 'XXXXXX3210'}
{'name': 'Customer 2', 'email': 'f***@mailbox.in', 'phone': 'XXXXXX2334'}

The rows here are invented for the example. Masking is a step in a controlled process, run by people allowed to see the original data, before anything is copied to a test system.

Tricky inputs find bugs fast

Experienced testers keep a personal list of inputs that often break software: empty strings, spaces only, apostrophes, hyphens, non-English scripts, very long text, leading zeros, negative numbers, dates such as 29 February. Run this name validator against such a list:

JavaScriptRun it yourself
function isValidName(name) {
  return /^[A-Za-z ]+$/.test(name);
}

const trickyNames = [
  "Priya Nair",       // normal
  "",                 // empty
  "   ",              // only spaces
  "D'Souza",          // apostrophe
  "Mohan-Lal",        // hyphen
  "प्रिया",            // Hindi script
  "A".repeat(300),    // very long
];

for (const name of trickyNames) {
  const label = name.length > 20 ? `${name.slice(0, 5)}... (${name.length} chars)` : JSON.stringify(name);
  console.log(`${isValidName(name) ? "accepted" : "REJECTED"}  ${label}`);
}
Output
accepted  "Priya Nair"
REJECTED  ""
accepted  "   "
REJECTED  "D'Souza"
REJECTED  "Mohan-Lal"
REJECTED  "प्रिया"
accepted  AAAAA... (300 chars)

Seven inputs revealed four defects: real names with apostrophes, hyphens and Indian scripts are refused, while a name of only spaces and a 300-character name are accepted. None of these would show up if you only tested "Priya Nair".

Managing test data

  • Keep data separate from test steps, in CSV or JSON files, so the same test can run with new data.
  • Reset state before each test. Create the account, cart or coupon the test needs at the start, and clean up after. Tests that share data break each other.
  • Make data unique where the system requires it (emails, phone numbers, order IDs), for example by adding a counter or timestamp.
  • Version your data files with your test code, so everyone runs the same data.
  • Document special accounts: "user locked1@example.com is always locked" saves your team hours.

Common mistakes

  • Testing with one perfect value. Real users type spaces, capital letters, apostrophes and very long text.
  • Random data with no seed. A failure you cannot reproduce wastes everyone's time.
  • Unmasked production data in test systems. It is a privacy and legal risk.
  • Tests that depend on leftover data. If a test needs a record, the test should create it.

Key points

  • Test data includes valid, invalid, boundary, empty, special-character, volume and state data.
  • Synthetic data is safe and flexible; a fixed random seed makes it repeatable.
  • Real personal data must be masked or avoided in test environments.
  • A short list of tricky inputs (spaces, apostrophes, other scripts, very long text) finds many defects.
  • Keep data separate from steps, reset state for every test, and version your data files.

Exercise

Rewrite isValidName() so that it accepts letters from any language, spaces, apostrophes and hyphens, rejects empty or spaces-only names, and rejects names longer than 100 characters. Hint: the regular expression /^\p{L}[\p{L}\p{M} '-]*$/u matches a letter followed by letters, vowel signs, spaces, apostrophes or hyphens in any script. Run it on the same tricky list.

Show solution
JavaScript
function isValidName(name) {
  const trimmed = name.trim();
  if (trimmed.length === 0 || trimmed.length > 100) return false;
  return /^\p{L}[\p{L}\p{M} '-]*$/u.test(trimmed);
}

const trickyNames = ["Priya Nair", "", "   ", "D'Souza", "Mohan-Lal", "प्रिया", "A".repeat(300)];
for (const name of trickyNames) {
  console.log(isValidName(name) ? "accepted" : "REJECTED", JSON.stringify(name.slice(0, 12)));
}
Output
accepted "Priya Nair"
REJECTED ""
REJECTED "   "
accepted "D'Souza"
accepted "Mohan-Lal"
accepted "प्रिया"
REJECTED "AAAAAAAAAAAA"

The u flag turns on Unicode mode and \p{L} means "any letter in any script". Indian scripts also use vowel signs (matras) such as the ि in प्रिया, which Unicode counts as marks, not letters, so \p{M} is needed too. Without it the Hindi name is still refused, which is exactly the kind of bug tricky test data exposes. Trimming first stops a spaces-only name from passing.

Quick Quiz

1. Why do testers set a random seed when generating test data?

2. What should happen to real customer data before it is used in a test environment?

3. A form accepts a name made only of spaces. What kind of test data found this?

4. Why add a counter to generated email addresses?

Learn Software Testing with a mentor

Free lessons get you started. Our Software Testing training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call