Skip to main content

What Makes a Great Test?

You know how to create a test now, but what separates a great test from one that constantly breaks or misses bugs? The difference isn’t just what you test—it’s how you structure your test. Think of it like building a house. You could throw materials together and technically have a structure, but a well-architected house with a solid foundation, proper framing, and quality materials will stand strong for decades. The same principle applies to tests. In this guide, you’ll learn the principles that make tests reliable, maintainable, and valuable.

The Anatomy of a Well-Structured Test

Every great test has four essential parts:

1. Setup (Getting to the Starting Point)

This is where you prepare your test environment and navigate to where you need to be. Example:
  • Log in to the application
  • Navigate to the shopping cart
  • Ensure you’re starting from a clean state

2. Actions (The Behavior You’re Testing)

These are the steps that simulate what a user actually does. Example:
  • Click “Add to Cart”
  • Change quantity to 3
  • Click “Proceed to Checkout”

3. Verifications (Checking Results)

This is where you confirm the application behaved correctly. Example:
  • Verify cart badge shows “3 items”
  • Verify total price is correct
  • Verify checkout page loaded

4. Cleanup (Optional)

Sometimes you need to clean up after a test (delete test data, log out, etc.). In many cases, Autonoma handles this automatically between test runs.

Principle 1: Keep Tests Short and Focused

Why Short Tests Matter

The problem with long tests: Imagine a 50-step test that checks your entire e-commerce flow from browsing to purchase confirmation. When it fails on step 37, you have to wade through dozens of steps to understand what went wrong. The better approach: Break it into focused tests:
  • Test 1: “Browse products and add to cart” (10 steps)
  • Test 2: “Update cart quantities” (8 steps)
  • Test 3: “Complete checkout flow” (12 steps)
When “Complete checkout flow” fails, you immediately know the problem is in checkout, not browsing or cart management.

The Goldilocks Principle

  • Too short (3-4 steps): Probably not testing a complete user journey
  • Too long (40+ steps): Hard to debug, slow to run, tests too much at once
  • Just right (8-20 steps): Tests one complete workflow, easy to understand and debug

Real-World Example

Bad (too long):
Test: "Complete user journey from signup to purchase"
- Create account (8 steps)
- Browse products (12 steps)
- Add to cart (6 steps)
- Apply discount code (4 steps)
- Complete checkout (15 steps)
- Verify email confirmation (3 steps)
Total: 48 steps
Good (focused tests):
Test 1: "User registration flow" (8 steps)
Test 2: "Product browsing and filtering" (12 steps)
Test 3: "Add items to cart" (6 steps)
Test 4: "Apply discount code at checkout" (8 steps)
Test 5: "Complete purchase" (10 steps)
When Test 4 fails, you know immediately that the discount code functionality is broken, not something in registration or browsing.

Principle 2: Assert Frequently

What Are Assertions?

An assertion (or verification) is a checkpoint in your test that says: “At this point, I expect to see X. If I don’t see it, something is wrong.” Without assertions, your test just clicks around without actually verifying anything works.

The Assertion Mindset

Think of assertions like checkpoints in a road trip:
  • You start in New York (Setup)
  • You should pass through Philadelphia (Assertion 1)
  • Then Baltimore (Assertion 2)
  • Then Washington DC (Assertion 3)
  • Finally arrive in Richmond (Final Assertion)
If you only check that you arrived in Richmond, you won’t know if you accidentally took a wrong turn through Pittsburgh and got lucky at the end.

When to Add Assertions

Add assertions after every significant action:
1. Click "Add to Cart"
   → Verify: Cart badge updates to "1"

2. Click cart icon
   → Verify: Cart page opens
   → Verify: Product is shown in cart

3. Click "Remove item"
   → Verify: Cart is empty
   → Verify: "Your cart is empty" message appears

Bad vs. Good Assertion Patterns

Bad (too few assertions):
1. Log in
2. Navigate to settings
3. Click "Delete Account"
4. Click "Confirm"
5. Verify: Account deleted message
Problem: If the test passes but the login didn’t actually work (it somehow got past the login screen), or if the settings page didn’t load properly, you won’t catch it until step 5 fails—or worse, the test might accidentally pass without actually testing anything. Good (frequent assertions):
1. Log in
   → Verify: Dashboard is visible
   → Verify: User name appears in header
2. Navigate to settings
   → Verify: Settings page loaded
   → Verify: "Delete Account" button is visible
3. Click "Delete Account"
   → Verify: Confirmation modal appears
4. Click "Confirm"
   → Verify: Account deleted message
   → Verify: Redirected to login page
Each assertion gives you confidence that you’re in the right place before continuing.

Principle 3: Understanding Dynamic Content

What is Dynamic Content?

Dynamic content is anything in your application that changes:
  • Loading spinners that appear and disappear
  • Data fetched from an API that takes time to load
  • Notifications that auto-dismiss
  • Real-time updates
  • Content that differs based on time/location/user
This is where many tests break, because they don’t account for these changes.

Why Dynamic Content Needs Special Attention

The problem: Your test runs in milliseconds, but your application might take seconds to load data. Common failure scenario:
1. Click "Load More Products"
2. Verify: 20 products are shown
   ❌ Test fails: Only 10 products visible (data still loading)
The application works fine—your test just didn’t wait for the data to load.

Strategies for Handling Dynamic Content

Strategy 1: Assert on Dynamic Content Explicitly

Instead of assuming content is there, verify it appears: Instead of:
1. Click "Load More"
2. Scroll to bottom
3. Click next product
Do this:
1. Click "Load More"
2. Verify: Loading spinner appears
3. Verify: Loading spinner disappears
4. Verify: 20 products are shown
5. Scroll to bottom
6. Click next product
This ensures you’re interacting with fully-loaded content.

Strategy 2: Use Waits for Loading States

Autonoma provides intelligent waiting mechanisms:
1. Click "Submit Order"
2. Wait until: "Order Confirmation" text appears
3. Verify: Order number is displayed
The test will automatically wait (up to a reasonable timeout) for the content to appear.

Strategy 3: Prefer Stable Content for Assertions

Bad (flaky):
Verify: "Last updated: 2:34 PM" appears
This breaks every time it runs because the time changes. Good (stable):
Verify: "Last updated" text appears
This checks that the update mechanism works without depending on the exact time.

Real-World Example: Testing a Dashboard

Bad approach (doesn’t handle dynamic content):
1. Log in
2. Verify: 5 notifications shown
3. Click first notification
Problems:
  • Number of notifications might vary
  • Notifications might still be loading
  • No verification that login actually completed
Good approach (handles dynamic content):
1. Log in
2. Verify: Dashboard header appears
3. Verify: Notifications section is visible
4. Wait until: Notification count badge appears
5. Verify: At least 1 notification exists
6. Click first notification
7. Verify: Notification detail panel opens

Principle 4: Overuse Waits and Scrolls

This might sound counterintuitive, but it’s one of the most important principles for robust tests.

Why Waits and Scrolls Matter

Modern web and mobile applications are complex:
  • Content loads asynchronously
  • Elements appear at different times
  • Infinite scroll patterns
  • Progressive loading
  • Animations and transitions
Traditional testing tools handle this poorly—you have to hard-code wait times like “wait 5 seconds,” which is either too long (slow tests) or too short (flaky tests).

Autonoma’s Intelligent Waits

Autonoma provides AI-assisted waits that are much smarter:

waitUntil - Wait for Conditions

Instead of guessing how long something takes, tell Autonoma what you’re waiting for: Traditional approach (fragile):
1. Click "Load Data"
2. Wait 3 seconds
3. Verify data appears
Problem: Sometimes it loads in 1 second (you wasted 2 seconds). Sometimes it takes 4 seconds (test fails). Autonoma approach (robust):
1. Click "Load Data"
2. Wait until: Data table appears
3. Verify: At least 10 rows shown
Autonoma waits only as long as needed, up to a reasonable timeout.

scrollUntil - Scroll to Find Content

For long pages or infinite scroll:
1. Navigate to product catalog
2. Scroll until: "Nike Air Max" product appears
3. Click on "Nike Air Max"
4. Verify: Product detail page loads
The AI understands what you’re looking for and scrolls intelligently to find it, even if the page layout changes.

When to Use Waits

Use waits whenever you:
  • Click a button that triggers loading
  • Submit a form that processes on the backend
  • Navigate to a new page
  • Trigger an animation or transition
  • Load data from an API
  • Wait for a modal to appear
Real-world example:
Test: "Submit contact form"

1. Fill in name field
2. Fill in email field
3. Fill in message field
4. Click "Submit"
5. Wait until: Loading spinner appears
6. Wait until: Loading spinner disappears
7. Wait until: "Message sent successfully" appears
8. Verify: Form fields are cleared
9. Verify: Success message is displayed
Steps 5-7 might seem excessive, but they make your test incredibly robust against varying network conditions and backend processing times.

When to Use Scrolls

Use scrolls when:
  • Content is below the fold
  • Testing infinite scroll features
  • Navigating long forms
  • Finding elements in long lists
Example:
Test: "Find and click footer link"

1. Navigate to homepage
2. Scroll until: Footer is visible
3. Verify: "Privacy Policy" link appears
4. Click "Privacy Policy"
5. Verify: Privacy policy page loads

The “Overuse” Philosophy

Counter-intuitive truth: It’s better to have too many waits than too few. A wait that’s unnecessary: Completes instantly (no harm done) A missing wait: Causes flaky test failures that waste hours of debugging Example of “overusing” waits (recommended):
1. Click "New Post"
2. Wait until: Post editor appears
3. Type post title
4. Wait until: Title is saved (auto-save indicator)
5. Type post content
6. Wait until: Content is saved
7. Click "Publish"
8. Wait until: Publishing progress bar appears
9. Wait until: Publishing progress bar disappears
10. Wait until: "Published successfully" message appears
11. Verify: Post appears in feed
This might look excessive, but it creates an incredibly reliable test that works regardless of network speed, server load, or timing variations.

Principle 5: Use Descriptive Prompts

Why Prompt Clarity Matters

When you come back to a test three months later (or when a colleague tries to understand it), clear prompts make all the difference.

Bad vs. Good Prompts

Bad:
1. Click button
2. Type text
3. Click
4. Verify message
Good:
1. Click "Add to Cart" button
2. Type "[email protected]" in email field
3. Click "Proceed to Checkout"
4. Verify "Order confirmation" message appears
The good version:
  • You can understand the test flow without running it
  • You can spot errors in logic just by reading
  • You can debug failures faster
  • Others can modify the test confidently

Naming Conventions for Prompts

For clicks: Include what you’re clicking
  • “Click the submit button”
  • “Click the ‘Create Account’ link”
  • “Click the product image”
For typing: Include what you’re typing and where
  • “Type ‘[email protected]’ in email field”
  • “Type ‘12345’ in zipcode field”
  • “Type ‘John Doe’ in cardholder name”
For verifications: Be specific about what should appear
  • “Verify ‘Success!’ message appears”
  • “Verify cart badge shows ‘3 items’”
  • “Verify product name ‘Blue T-Shirt’ is displayed”
For waits: Describe what you’re waiting for
  • “Wait until loading spinner disappears”
  • “Wait until product list loads”
  • “Wait until checkout page appears”

Putting It All Together: Before and After

Let’s see how these principles transform a real test:

Before (Poorly Structured)

Test: "Buy product"

1. Go to site
2. Click
3. Click
4. Click
5. Type text
6. Click
7. Type text
8. Type text
9. Click
10. Wait 5 seconds
11. Verify success
Problems:
  • Can’t tell what’s being tested
  • No intermediate verifications
  • Hard-coded wait time
  • No handling of dynamic content
  • Impossible to debug when it fails

After (Well Structured)

Test: "Checkout - Purchase single product with credit card"

Setup:
1. Log in as test user
2. Verify: Dashboard loads
3. Navigate to products page
4. Verify: Product grid appears

Actions:
5. Click "Blue T-Shirt" product
6. Verify: Product detail page loads
7. Click "Add to Cart"
8. Verify: Cart badge shows "1"
9. Click cart icon
10. Verify: Cart page opens
11. Verify: "Blue T-Shirt" appears in cart
12. Click "Proceed to Checkout"
13. Verify: Checkout form appears

14. Fill in email: "[email protected]"
15. Fill in card number: "4242 4242 4242 4242"
16. Fill in expiry: "12/25"
17. Fill in CVC: "123"

18. Click "Complete Purchase"
19. Wait until: Loading spinner appears
20. Wait until: Loading spinner disappears
21. Wait until: "Order confirmation" page appears

Verification:
22. Verify: "Thank you for your order" message
23. Verify: Order number is displayed
24. Verify: Email confirmation message appears
Improvements:
  • Clear test purpose in the name
  • Organized into logical sections
  • Frequent verifications after each key action
  • Intelligent waits instead of hard-coded delays
  • Descriptive prompts that explain every step
  • Easy to understand and maintain

Common Pitfalls to Avoid

Pitfall 1: Testing Too Much in One Test

Problem: “Test everything in one mega-test” Solution: Break into focused, single-purpose tests

Pitfall 2: No Verifications Until the End

Problem: Only checking results at the end Solution: Verify after each significant action

Pitfall 3: Hard-Coding Dynamic Values

Problem: Verify: "Updated at 3:42 PM" Solution: Verify: "Updated at" text appears

Pitfall 4: Not Waiting for Async Operations

Problem: Clicking before elements load Solution: Use waitUntil liberally

Pitfall 5: Vague Prompts

Problem: “Click button”, “Verify message” Solution: “Click the ‘Submit’ button”, “Verify ‘Success!’ message appears”

Quick Reference: Test Structure Checklist

Use this checklist when creating or reviewing tests:
  • Test name clearly describes what’s being tested
  • Test is focused (one feature/workflow)
  • Test is reasonably short (under 30 steps ideally)
  • Verifications after every significant action
  • Waits used for all async operations
  • Scrolls used when accessing below-the-fold content
  • All prompts are specific and descriptive
  • Dynamic content is handled properly
  • Setup phase gets test to starting point
  • Cleanup is performed (if needed)

Key Takeaways

  1. Keep tests short and focused - one workflow per test
  2. Assert frequently - verify results after every significant action
  3. Handle dynamic content explicitly - don’t assume things have loaded
  4. Overuse waits and scrolls - better too many than too few
  5. Use descriptive prompts - your future self will thank you
  6. Structure tests logically - setup, actions, verifications, cleanup

What’s Next

Now that you understand what makes tests robust and maintainable, the next guide will show you how to organize multiple tests effectively using folders, naming conventions, and tags.