How to Write Test Cases for Profile Editing (With Examples)
How to Write Test Cases for Profile Editing (With Examples)
How to Write Test Cases for Profile Editing (With Examples)
Writing effective test cases for a profile editing feature is a core skill for QA engineers and developers who ship reliable applications. A well‑crafted test suite catches validation bugs, data‑corruption issues, and usability friction before they reach users. This guide walks you through the anatomy of a test case, how to categorize them, a concrete matrix of 20+ examples, data‑setup strategies, prioritization methods, manual‑vs‑automated approaches, how autonomous exploration complements manual design, a review checklist, production‑only edge cases, and finally, habits for continuous improvement. By the end you will have a ready‑to‑use test‑case template and a clear process to keep your profile‑editing coverage high‑signal and low‑maintenance.
Foundations of a Good Test Case
Every test case should be self‑contained, readable, and repeatable. The following elements form the minimal viable structure that works in test‑management tools (TestRail, Zephyr, Xray) and in plain‑text markdown files.
Test Case ID and Naming Conventions
A stable identifier lets you trace results back to requirements and aggregate metrics. Use a hierarchical scheme: . For profile editing, a good ID looks like PROF_EDIT_001. Keep the numeric part zero‑padded to three digits so sorting stays logical. The name should read like a sentence: “Verify that a user can update their display name with a valid string”. Avoid vague names such as “Test name edit”; they add noise during triage.
Preconditions and Test Data Setup
Preconditions describe the state the system must be in before the first step. For profile editing, typical preconditions include:
- A registered user exists with known credentials.
- The user is logged in and on the profile screen.
- Any dependent services (e.g., avatar storage, email verification API) are mocked or available.
Document the exact data needed: user ID, email, password, and any existing profile values. If you rely on a fixture, reference it by name (e.g., user_fixture.json). This makes the case portable across environments and reduces “works on my machine” failures.
Steps, Expected Results, and Postconditions
Steps should be imperative, atomic, and free of implementation details that might change. Number them and keep each step to a single user action or verification. Example:
- Tap the Edit button.
- Clear the Display Name field.
- Enter “Ada Lovelace”.
- Tap Save.
Expected results must be observable and testable. Instead of “the name updates”, write “The profile header displays “Ada Lovelace” and the underlying API returns {"display_name":"Ada Lovelace”} with HTTP 200. Postconditions (optional) note any cleanup required, such as reverting the field to its original value or deleting a test‑generated avatar. Clear postconditions prevent state leakage between test runs.
Categorizing Tests: Positive, Negative, Boundary, Edge Cases
A balanced suite covers happy paths, validation failures, limits, and unusual inputs. Categorizing early helps you spot gaps and allocate effort efficiently.
Positive Flow Tests
These confirm that the feature works when everything is correct. Typical positive cases:
- Updating each editable field (name, email, phone, bio, avatar) with a valid value.
- Successfully canceling an edit and seeing the original values restored.
- Submitting a form after making multiple changes in one session.
- Receiving a success toast or inline confirmation after save.
Negative Validation Tests
Validation is where most bugs hide. For each field, define the invalid inputs your spec rejects and write a case for each:
- Empty required fields.
- Email without
@or with multiple@. - Phone number containing letters or exceeding the country‑specific length.
- Bio longer than the allowed character limit.
- Uploading an avatar with an unsupported MIME type (e.g.,
.exe) or exceeding the size cap.
When writing negative cases, include the expected error message or inline validation text. This makes the test assertive rather than relying on a generic “error shown”.
Boundary and Length Tests
Boundary testing zeroes in on the limits defined in the spec. If the display name allows 2–30 characters, test:
- 1 character (should fail).
- 2 characters (should pass).
- 30 characters (should pass).
- 31 characters (should fail).
Do the same for numeric ranges (e.g., age 13–120) and file sizes (avatar ≤ 2 MB). Boundary tests often uncover off‑by‑one bugs that slip through functional testing.
Edge Cases (Special Characters, Unicode, etc.)
Real users paste emojis, zero‑width spaces, or right‑to‑left language strings. Edge‑case tests verify that the UI handles them without crashing or corrupting data. Examples:
- Name containing emojis (😀) or combined characters (é).
- Bio with a mixture of Latin, Cyrillic, and Chinese characters.
- Phone number prefixed with
+and spaces. - Avatar file name with Unicode characters.
Also test interruptions: losing network mid‑save, receiving an incoming call, or the device going to sleep while the edit screen is open. These scenarios often surface only in production but can be approximated with device‑state manipulation tools.
Building a Test Matrix for Profile Editing
Below is a concrete matrix of 22 test cases that covers the categories above. Feel free to copy‑paste it into your test‑management tool; adjust IDs and preconditions to match your project’s naming scheme.
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| PROF_EDIT_001 | User alice@example.com logged in; profile shows name “Alice Smith”. | 1. Tap Edit. 2. Change Display Name to “Ada Lovelace”. 3. Tap Save. | Header shows “Ada Lovelace”. API PATCH /users/{id} returns 200 with "display_name":"Ada Lovelace". |
| PROF_EDIT_002 | Same as 001. | 1. Tap Edit. 2. Clear Display Name. 3. Tap Save. | Inline error: “Display name is required”. No API call made. |
| PROF_EDIT_003 | Same as 001. | 1. Tap Edit. 2. Set Display Name to “A”. 3. Tap Save. | Inline error: “Display name must be at least 2 characters”. |
| PROF_EDIT_004 | Same as 001. | 1. Tap Edit. 2. Set Display Name to a 30‑character string. 3. Tap Save. | Name updates successfully; API returns 200. |
| PROF_EDIT_005 | Same as 001. | 1. Tap Edit. 2. Set Display Name to a 31‑character string. 3. Tap Save. | Inline error: “Display name must not exceed 30 characters”. |
| PROF_EDIT_006 | User logged in; email alice@example.com. | 1. Tap Edit. 2. Change Email to newemail@domain.co. 3. Tap Save. | Email updates; confirmation email sent to new address; API returns 200. |
| PROF_EDIT_007 | Same as 006. | 1. Tap Edit. 2. Change Email to invalidemail. 3. Tap Save. | Inline error: “Enter a valid email address”. |
| PROF_EDIT_008 | Same as 006. | 1. Tap Edit. 2. Change Email to alice@example.com (original). 3. Tap Save. | No change; API returns 200 with same email. |
| PROF_EDIT_009 | User logged in; phone +1 555‑123‑4567. | 1. Tap Edit. 2. Change Phone to +44 7911 123456. 3. Tap Save. | Phone updates; API returns 200. |
| PROF_EDIT_010 | Same as 009. | 1. Tap Edit. 2. Change Phone to abc-def-ghij. 3. Tap Save. | Inline error: “Phone number must contain only digits, spaces, +, -, (, )”. |
| PROF_EDIT_011 | Same as 009. | 1. Tap Edit. 2. Change Phone to a 16‑digit number. 3. Tap Save. | Inline error: “Phone number too long”. |
| PROF_EDIT_012 | User logged in; bio empty. | 1. Tap Edit. 2. Enter bio: “I love testing ☕🚀”. 3. Tap Save. | Bio updates; emojis preserved; API returns 200. |
| PROF_EDIT_013 | Same as 012. | 1. Tap Edit. 2. Enter 250‑character bio (max). 3. Tap Save. | Bio updates; API returns 200. |
| PROF_EDIT_014 | Same as 012. | 1. Tap Edit. 2. Enter 251‑character bio. 3. Tap Save. | Inline error: “Bio must be 250 characters or less”. |
| PROF_EDIT_015 | User logged in; no avatar set. | 1. Tap Edit. 2. Tap Change Avatar → select a 1.5 MB PNG. 3. Tap Save. | Avatar updates; preview shows image; API returns 200 with URL. |
| PROF_EDIT_016 | Same as 015. | 1. Tap Edit. 2. Tap Change Avatar → select a 3 MB file. 3. Tap Save. | Inline error: “Avatar size must be ≤ 2 MB”. |
| PROF_EDIT_017 | Same as 015. | 1. Tap Edit. 2. Tap Change Avatar → select a .exe file. 3. Tap Save. | Inline error: “Only image files (JPG, PNG, GIF) are allowed”. |
| PROF_EDIT_018 | User logged in; on profile screen. | 1. Tap Edit. 2. Make any change. 3. Tap Cancel (or back). | No changes persisted; original values displayed. |
| PROF_EDIT_019 | User logged in; edit screen open. | 1. Begin editing name. 2. Simulate loss of network (Airplane mode) before tapping Save. 3. Tap Save. | Toast: “Failed to save – check connection”. Data remains unchanged on server. |
| PROF_EDIT_020 | User logged in; edit screen open. | 1. Start editing. 2. Receive an incoming call (simulate via adb). 3. After call ends, return to app. | Edit screen retains entered values; user can continue or cancel without data loss. |
| PROF_EDIT_021 | User logged in; background sync ongoing. | 1. Trigger a profile sync (e.g., change elsewhere). 2. While sync indicator shows, open edit screen and change name. 3. Tap Save. | Save queues correctly; after sync finishes, name reflects the latest edit; no conflict errors. |
| PROF_EDIT_022 | User logged in; avatar URL from CDN. | 1. Tap Edit. 2. Remove avatar (tap Delete Avatar). 3. Tap Save. | Avatar reverts to default placeholder; API returns 200 with avatar_url:null. |
How to use the matrix
- Copy the table into a spreadsheet or test‑case tool.
- Map each ID to a requirement or user story (see Traceability section).
- Tag each row with a type (Positive/Negative/Boundary/Edge) for quick filtering.
- When adding a new field (e.g., “Date of Birth”), replicate the pattern: valid, empty, min, max, invalid format.
Data Management and Test Data Strategies
Robust test data eliminates flaky tests and speeds up execution. Decide early whether you will use static fixtures, dynamically generated data, or a hybrid approach.
Static vs Dynamic Data
Static fixtures (JSON, CSV, YAML) are great for deterministic negative cases where you need exact values (e.g., an invalid email). Keep them under version control and name them descriptively (invalid_email_list.json). Dynamic data, generated at runtime via factories or APIs, is ideal for positive cases where uniqueness matters (e.g., a new email that must not clash with existing accounts). Use a combination: static for validation, dynamic for happy paths.
Using Data Pools and Parameterization
Many test frameworks support data‑driven testing. In JUnit 5 you can annotate a method with @ParameterizedTest and feed it a CSVSource. In pytest, use @pytest.mark.parametrize. This lets you write a single test step that iterates over a list of inputs, reducing duplication. Example in Java (JUnit 5):
@ParameterizedTest
@CsvSource({
"'', 'Display name is required'",
"'A', 'Display name must be at least 2 characters'",
"'Ada Lovelace', null"
})
void testDisplayNameValidation(String input, String expectedError) {
// navigate to edit screen, set field, tap save
String actualError = profilePage.getDisplayNameError();
assertEquals(expectedError, actualError);
}
Mock Services and Stubbed APIs
When the backend is unavailable or slow, stub the network layer with tools like MockWire, WireMock, or MSW (Mock Service Worker). Define endpoints for PATCH /users/{id} and GET /users/{id} with configurable responses (success, 400, 500). This lets you test error‑handling paths without relying on a flaky dev server. Store stub definitions alongside your test cases so that a new tester can spin up the environment with a single command:
# start WireMock on port 8080
java -jar wiremock.jar --port 8080 --verbose
Code Snippet: JSON Fixture for a Base User
Store this as testdata/base_user.json and load it in your test setup:
{
"id": "u_12345",
"email": "alice@example.com",
"password": "SecureP@ssw0rd!",
"profile": {
"display_name": "Alice Smith",
"phone": "+1 555-123-4567",
"bio": "",
"avatar_url": null
}
}
In your test initialization, deserialize this JSON, create the user via API (or use a pre‑seeded test database), then log in. This guarantees a known starting point for every test case.
Prioritization and Traceability
Not all test cases carry the same weight. Prioritizing based on risk and linking each case to requirements ensures you focus effort where failures hurt the most and you can demonstrate coverage to stakeholders.
Risk‑Based Prioritization (Impact/Likelihood)
Assign a priority level (P0‑P3) using a simple matrix:
| Priority | Definition | Typical Criteria for Profile Editing |
|---|---|---|
| P0 | Must‑have; blocks release | Required fields, successful save, critical security (e.g., email change verification) |
| P1 | High impact; should be caught before release | Validation messages, boundary limits, cancel behavior |
| P2 | Medium impact; acceptable to find post‑release | Edge‑case Unicode, avatar size limits, network loss handling |
| P3 | Low impact; nice to have | Animations, toast wording, minor UI alignment |
When you create a test case, add a Priority column. During test planning, execute all P0 and P1 cases first; schedule P2/P3 for later cycles or as time permits.
Linking to Requirements (Req IDs, User Stories)
Each test case should reference at least one requirement identifier. If you use Jira, include the issue key (e.g., PROJ-452). In a markdown file you can add a comment:
# PROF_EDIT_007
# Req: PROJ-452 – Email format validation
This traceability enables impact analysis: when a requirement changes, you can quickly locate all related test cases via a search or a traceability matrix.
Maintaining a Traceability Matrix
A lightweight matrix lives in a spreadsheet or Confluence page:
| Req ID | Description | Linked Test Cases |
|---|---|---|
| PROJ-450 | User can update display name | PROF_EDIT_001, PROF_EDIT_003‑005 |
| PROJ-451 | Email must be valid and unique | PROF_EDIT_006‑008 |
| PROJ-452 | Phone number format validation | PROF_EDIT_009‑011 |
| PROJ-453 | Bio length limit 250 characters | PROF_EDIT_012‑014 |
| PROJ-454 | Avatar upload ≤ 2 MB, image only | PROF_EDIT_015‑017 |
| PROJ-455 | Cancel discards changes | PROF_EDIT_018 |
| PROJ-456 | Network loss handled gracefully | PROF_EDIT_019 |
| PROJ-457 | Edit persists through interruption | PROF_EDIT_020 |
| PROJ-458 | Concurrent sync handled | PROF_EDIT_021 |
| PROJ-459 | Avatar removal reverts to default | PROF_EDIT_022 |
Update this matrix whenever you add or retire a test case. It becomes a valuable artifact for audits and for showing test coverage to product managers.
Manual Execution vs Automated Scripts
Deciding which tests to automate hinges on stability, frequency of execution, and cost of maintenance. Profile editing contains both deterministic checks (ideal for automation) and exploratory scenarios (better left to manual or autonomous testing).
When to Keep Tests Manual
- Visual verification: Ensuring that an avatar appears correctly after upload, or that a toast appears in the right location, often requires human judgment.
- Interruption handling: Simulating an incoming call or low‑battery state reliably on emulators can be fiddly; a manual tester can use the device’s UI to trigger these events.
- Ad‑hoc exploratory checks: Trying unusual paste gestures, voice‑over navigation, or testing with accessibility tools benefits from a tester’s intuition.
Converting Test Cases to Automation (Appium – Android)
Below is a compact Appium Java test that covers the positive display‑name update (PROF_EDIT_001) and the empty‑field negative case (PROF_EDIT_002). It uses Page Object Model principles for readability.
public class ProfileEditTest {
private AndroidDriver driver;
private ProfileEditPage profilePage;
@BeforeEach
public void setUp() {
// Assuming you have a helper that returns a configured driver
driver = DriverFactory.getDriver();
profilePage = new ProfileEditPage(driver);
// login as alice@example.com / SecureP@ssw0rd!
loginHelper.login("alice@example.com", "SecureP@ssw0rd!");
profilePage.open();
}
@Test
@DisplayName("PROF_EDIT_001 – Valid display name update")
public void testValidDisplayNameUpdate() {
profilePage tapEditButton();
profilePage.clearDisplayName();
profilePage.setDisplayName("Ada Lovelace");
profilePage tapSaveButton();
assertEquals("Ada Lovelace", profilePage.getDisplayedName());
// optional API verification via RestAssured
assertTrue(apiHelper.getUserProfile().getDisplayName().equals("Ada Lovelace"));
}
@Test
@DisplayName("PROF_EDIT_002 – Empty display name error")
public void testEmptyDisplayNameError() {
profilePage tapEditButton();
profilePage.clearDisplayName();
profilePage tapSaveButton();
assertEquals("Display name is required", profilePage.getDisplayNameError());
}
}
Page Object snippet (only relevant methods shown):
public class ProfileEditPage {
private final AndroidDriver driver;
private By editBtn = By.id("edit_profile_button");
private By nameField = By.id("input_display_name");
private By saveBtn = By.id("button_save");
private By nameError = By.id("text_input_layout_display_name_error");
private By displayedName = By.id("text_view_profile_name");
public ProfileEditPage(AndroidDriver driver) {
this.driver = driver;
}
public void tapEditButton() { driver.findElement(editBtn).click(); }
public void clearDisplayName() {
driver.findElement(nameField).clear();
}
public void setDisplayName(String text) {
driver.findElement(nameField).sendKeys(text);
}
public void tapSaveButton() { driver.findElement(saveBtn).click(); }
public String getDisplayedName() {
return driver.findElement(displayedName).getText();
}
public String getDisplayNameError() {
return driver.findElement(nameError).getText();
}
}
Converting Test Cases to Automation (Playwright – Web)
If your profile editor is a web SPA, Playwright offers a concise TypeScript version:
import { test, expect } from '@playwright/test';
test.describe('Profile Editing', () => {
test.use({ storageState: 'state/alice.json' }); // pre‑logged‑in state
test('PROF_EDIT_001 – Update display name', async ({ page }) => {
await page.goto('/profile');
await page.click('button:has-text("Edit")');
await page.fill('input[name="displayName"]', 'Ada Lovelace');
await page.click('button:has-text("Save")');
await expect(page.locator('.profile-name')).toHaveText('Ada Lovelace');
// optional API check
const [response] = await Promise.all([
page.waitForResponse(resp => resp.url().endsWith('/users/me') && resp.status() === 200),
page.click('button:has-text("Save")')
]);
const json = await response.json();
expect(json.display_name).toBe('Ada Lovelace');
});
test('PROF_EDIT_002 – Empty display name shows error', async ({ page }) => {
await page.goto('/profile');
await page.click('button:has-text("Edit")');
await page.fill('input[name="displayName"]', '');
await page.click('button:has-text("Save")');
await expect(page.locator('.error-displayName')).toHaveText('Display name is required');
});
});
Both snippets demonstrate how a single test case maps to a few lines of code. Keep your test classes small; each class should correspond to a logical screen or component.
Maintaining Sync Between Manual and Automated Suites
- Tagging: In your test‑management tool, add tags like
@automatedor@manual. When you automate a case, move the tag. - Review cadence: Every sprint, run a “sync review” where the QA lead compares the automated test list against the manual test matrix and flags any gaps.
- Fail‑fast feedback: If an automated test starts failing frequently due to flakiness, evaluate whether it should be reverted to manual until the underlying issue (e.g., unstable test data) is resolved.
Leveraging Autonomous Exploration with SUSA
Autonomous testing tools can surface scenarios that are tedious to enumerate manually, especially in complex flows like profile editing where multiple fields interact and system states (network, permissions, background tasks) vary.
How SUSA Discovers Profile Editing Flows
When you point SUSA at your Android APK or web URL, it builds a state‑machine of screens by exercising taps, swipes, text entry, and dialog handling. For profile editing, SUSA will:
- Navigate to the profile screen from various entry points (home drawer, settings, deep link).
- Attempt to edit every detectable field, trying both valid and invalid values derived from its built‑in heuristics (e.g., it tries extremely long strings, special Unicode, emojis).
- Trigger interruptions such as losing Wi‑Fi, rotating the device, or receiving a push notification while the edit screen is open.
- Record each distinct path and annotate it with observed outcomes (crash, ANR, validation toast, success).
The output is a set of explored flows, each with a PASS/FAIL verdict based on heuristics like “no uncaught exception” and “UI element present as expected”.
Augmenting Manual Cases with Machine‑Generated Paths
You can import SUSA’s flow report (usually a JSON or CSV) into your test‑management tool and map each discovered path to an existing test case ID. Any flow that does not match an existing case becomes a candidate for a new manual test or for automation. Example mapping:
| SUSA Flow ID | Matched Manual Case | Notes |
|---|---|---|
| flow_023 | PROF_EDIT_019 | Network loss mid‑save – already covered |
| flow_057 | — | Tried to paste a 500‑character string into bio; resulted in UI freeze – new edge case |
| flow_089 | PROF_EDIT_020 | Incoming call during edit – covered |
| flow_112 | — | Attempted to change email while a sync was in progress; got 409 conflict – new case |
By reviewing the unmatched flows periodically, you keep your test suite aligned with real‑world usage patterns that might not appear in specification documents.
Cross‑Session Learning and Regression Guardrails
SUSA remembers which screens it has already explored and which actions led to dead ends (e.g., a button that does nothing). On subsequent runs, it prioritizes novel interactions, gradually increasing coverage without exponential growth in execution time. For profile editing, this means:
- Early runs focus on basic field edits.
- Later runs try combinations: editing name *and* avatar simultaneously, or editing while a background download is happening.
- The tool builds a regression baseline: if a previously passing flow starts failing, SUSA flags it as a regression, giving you an early warning before the issue reaches users.
Tip: Schedule a nightly SUSA run against your staging build. Feed the new failures into your triage board as “candidate test cases”. This creates a feedback loop where autonomous exploration continuously enriches your manual test catalog.
Checklist for Reviewing Profile Editing Test Cases
A lightweight review checklist catches ambiguities, missing preconditions, and inconsistent phrasing before tests enter the execution queue. Use it during test‑case authoring and during periodic maintenance.
Pre‑Review Checklist (Clarity, Completeness)
| ✅ Item | Description |
|---|---|
ID follows format | Guarantees sortable, unique identifiers. |
| Name reads like a user action | E.g., “Verify that a user can update their display name with a valid string”. |
| Preconditions list all required setup | Login, existing data, mocked services, device state. |
| Steps are atomic and numbered | No combined actions like “enter name and tap save”. |
| Expected result is observable and testable |
Test Your App Autonomously
Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.
Try SUSA Free