Search by labels

Monday, December 22, 2025

Creating a Fully Running Test Automation Framework in Under 3 Minutes with Cursor

In this tutorial, you’ll generate a clean, production-ready web UI automation framework in under 3 minutes using Cursor—and you’ll validate a real-world scenario: a failed login on SauceDemo for a locked-out user.

I’ll also embed my screen recording in the post so you can follow the exact flow I used end-to-end.


What you’ll build

A working Gradle project using:

  • Java 17
  • JUnit 5
  • Selenide
  • Gradle (Groovy DSL)

With best-practice structure:

  • Page Object Model (POM)
  • Clear separation:
    • test logic
    • page interactions
    • configuration
  • External configuration:
    • base URL
    • credentials
  • A real test:
    • open https://www.saucedemo.com
    • login as locked_out_user
  • Verify the error message:

      Epic sadface: Sorry, this user has been locked out.




       


      Step 1: Create an empty folder + open it in Cursor

      1. Create a new folder, e.g. saucedemo-selenide-junit5

      2. Open it in Cursor

      3. Make sure Cursor can create files in the project (normal default)

      That’s all—no manual Gradle init needed if you’re letting Cursor generate everything.


      Step 2: Paste this prompt into Cursor

      This is the exact prompt the framework is based on (copy/paste as-is):

      You are a Senior QA Automation Engineer.

      Create a clean, production-ready web UI test automation framework using:

      - Java 17

      - JUnit 5

      - Selenide

      - Gradle (Groovy DSL)


      Architecture & Best Practices:

      - Follow Page Object Model (POM).

      Separate:

      - Test logic

      - Page interactions

      - Configuration


      Use Selenide best practices:

      - No explicit waits

      - Centralized browser configuration

      - Stable, readable locators

      - Configuration must be externalized (URL, credentials).

      - Code must be readable, maintainable, and scalable.


      Project Structure:

      src

      └── test

          ├── java

          │   ├── base

          │   │   └── BaseTest.java

          │   ├── config

          │   │   └── TestConfig.java

          │   ├── pages

          │   │   └── LoginPage.java

          │   └── tests

          │       └── LoginTest.java

          └── resources

              └── application.properties


      Functional Requirements:

      1. Open https://www.saucedemo.com

      2. Try to login using:

      Username: locked_out_user

      Password: secret_sauce

      3. Verify that login fails with error message 

      "Epic sadface: Sorry, this user has been locked out."


      What to Generate:

      - build.gradle with all required dependencies

      - Browser and timeout configuration

      - Base test setup using JUnit 5 lifecycle hooks

      - LoginPage Page Object with actions and assertions

      - LoginTest using JUnit 5 and Selenide assertions

      - application.properties for configuration


      Execution:

      The project must be runnable with:

      ./gradlew test


      Generate complete, runnable code for all files.

      Why this prompt works

      It forces Cursor to:

      • generate all files, not fragments
      • follow a known structure
      • implement a real assertion
      • externalize config (so you don’t hardcode URLs and creds into tests)


      Step 3: Make sure Cursor generates these files

      After Cursor responds, your repository should include at least:

      • build.gradle
      • src/test/java/base/BaseTest.java
      • src/test/java/config/TestConfig.java
      • src/test/java/pages/LoginPage.java
      • src/test/java/tests/LoginTest.java
      • src/test/resources/application.properties

      If you’re missing any of these, ask Cursor:

      “Create the missing files and ensure the project runs with ./gradlew test.”


      Step 4: What the code should do (high-level)

      TestConfig.java

      Central place to configure Selenide:

      • base URL
      • browser (optional)
      • timeout
      • screenshot / reports folder (optional)

      BaseTest.java

      JUnit 5 lifecycle hooks:

      • set up config once (or before each test)
      • optionally clean browser state

      LoginPage.java (Page Object)

      Encapsulates:

      • locators (username, password, login button, error message)
      • actions (open page, login)
      • assertions (error visible + exact text)

      LoginTest.java

      Reads like a scenario:

      • open login page
      • attempt login as locked out user
      • assert correct error is shown


      Step 5: Run it

      From the project root:

      ./gradlew test

      You should see:

      • Gradle downloads dependencies
      • Selenide launches a browser
      • test runs
      • test passes ✅ (because the expected error is displayed)


      Expected behavior on SauceDemo

      The locked_out_user is a known user on SauceDemo that always fails login with the message:

      Epic sadface: Sorry, this user has been locked out.

      Your test should assert the exact message, not something vague—this is important for stable UI test feedback.


      Final Thoughts

      What you’ve seen here isn’t magic — it’s leverage.

      Cursor didn’t “replace” test automation skills. It amplified them.
      The reason this worked in under 3 minutes is because the intent, structure, and expectations were clear from the start.

      The real takeaway is this:

      • If you know what good architecture looks like
      • If you can describe clean separation of concerns
      • If you understand how tests should read and behave

      …then tools like Cursor become a serious productivity multiplier rather than a code generator you have to babysit.

      This small example already gives you:

      • a maintainable project structure
      • a real negative test with a meaningful assertion
      • a foundation you can safely extend in a real project

      From here, scaling is easy:

      • add more page objects
      • add reporting
      • add CI
      • add parallelism

      The hardest part — getting started correctly — is already done.

      If this tutorial helped you, try repeating the exercise with:

      • a positive login flow
      • a different browser
      • a new assertion
      • or an entirely different application

      You’ll quickly notice that the speed gain compounds.

      The future of test automation isn’t writing less code — it’s spending less time on the wrong code.


      No comments:

      Post a Comment