Prompt-Engineer a QA Test Suite

Use Claude.ai to generate a full Cucumber test suite for a book search feature.

Introduction

30 Second Summary

Writing test cases from a feature spec is one of the most time-consuming parts of QA work. You read the requirements, imagine what could go wrong, and document dozens of scenarios by hand.

In this project, you will use prompt engineering techniques with Claude.ai to turn a plain-English feature spec into a complete test suite for an e-publishing site's book search feature. You will see a naive prompt fall short, then apply specific techniques that close the gap.

What You'll Build

Picture pasting your feature spec into Claude.ai, running a series of refined prompts, and walking away with structured manual test cases, a valid Gherkin feature file, and Java step definitions that compile on the first try.

By the end of this project, you'll have:

  • A reusable prompt engineering workflow that turns any feature spec into 20+ structured manual test cases with IDs, preconditions, steps, expected results, and priority.
  • A Cucumber feature file with declarative Gherkin scenarios generated through prompt chaining and few-shot examples, validated in your Eclipse project.
  • Java step definitions and a Page Object Model class using Selenium WebDriver that compile in your Maven project, all generated through engineered prompts.
  • Secret Mission: Generate data-driven Scenario Outline tests with parameterized search data covering edge cases and error conditions.

Are there any prerequisites?

This project assumes you are comfortable with Java, Cucumber, Gherkin, TestNG, and Selenium WebDriver. You should have JDK, Maven, and Eclipse IDE already installed on your machine.

Before We Start

Before any hands-on work, this is your moment to lock in what you're about to build and why it matters. You're going to prompt-engineer a complete test suite for a book search feature on an e-publishing site, and understanding the goal upfront makes every step that follows more intentional.

Set Up Your Project and Feature Spec

Before you can engineer prompts that produce production-quality test cases, you need two things: a real project that can validate whether generated code compiles, and a detailed feature specification for the LLM to work against.

In this step, you will create a Maven project in Eclipse with Cucumber, TestNG, and Selenium WebDriver dependencies. You will also write a plain-English feature spec that describes the book search functionality your prompts will target.

In this step, get ready to:
  • Create a Maven project in Eclipse with testing dependencies.
  • Set up the standard Cucumber directory structure.
  • Write a feature specification document for book search.
Create a Maven project and add dependencies
  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Eclipse and press Enter to open it.

Eclipse opens to your workspace. Now you will create a new Maven project to hold all of your test code.

  • Click File in the top menu bar.
  • Click New.
  • Click Other.
  • Type Maven Project in the filter field.
  • Select Maven Project from the results.
  • Click Next.

Why a Maven project?

Maven manages your project's dependencies and build lifecycle. When you add libraries like Cucumber and Selenium to the pom.xml file, Maven automatically downloads them.

More importantly, running mvn compile will tell you instantly whether code generated by your prompts actually compiles. That feedback loop is essential for validating LLM output.

  • Check the Create a simple project (skip archetype selection) checkbox.
  • Click Next.
  • Enter com.epublishing in the Group Id field.
  • Enter epublishing-tests in the Artifact Id field.
  • Click Finish.

Eclipse creates the project and you should see epublishing-tests appear in the Package Explorer on the left. The project includes a default pom.xml that you will now replace with your testing dependencies.

  • In the Package Explorer, double-click pom.xml to open it in the editor.
  • If the file opens in the Overview tab (a form view), click the pom.xml tab at the bottom of the editor to switch to the raw XML view.
  • Select all the existing content and delete it.
  • Paste the following XML, which sets up the project coordinates, compiler settings, and version properties:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.epublishing</groupId>
    <artifactId>epublishing-tests</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <!-- Compiler settings and version properties -->
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <cucumber.version>7.26.0</cucumber.version>
        <selenium.version>4.36.0</selenium.version>
        <testng.version>7.12.0</testng.version>
    </properties>

What does this code do?

The <properties> block centralises your dependency versions. Instead of typing 7.26.0 in multiple places, you define it once as ${cucumber.version} and reference it everywhere.

The compiler source and target are set to Java 11, which ensures your generated code compiles against a modern JDK.

  • Directly below the code you just pasted (after the closing </properties> tag), paste the dependencies and build plugin to complete the file:
    <!-- Test framework dependencies -->
    <dependencies>
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-java</artifactId>
            <version>${cucumber.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-testng</artifactId>
            <version>${cucumber.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>${testng.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>${selenium.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <!-- Build plugin for running tests -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.5.2</version>
            </plugin>
        </plugins>
    </build>
</project>

What does this code do?

  • cucumber-java provides the step definition annotations (@Given, @When, @Then) that map Gherkin steps to Java methods.
  • cucumber-testng integrates Cucumber with the TestNG test runner.
  • testng is the testing framework that provides assertions and test execution.
  • selenium-java is the browser automation library your Page Object classes will use.
  • maven-surefire-plugin is the build plugin that Maven uses to discover and run your test classes.
  • Save pom.xml by pressing Cmd+S (macOS) or Ctrl+S (Windows).

After saving, Eclipse automatically downloads the dependencies. You should see a progress indicator in the bottom-right corner of Eclipse. Wait for it to finish before continuing.

Seeing red error markers on pom.xml?

  • Check that you pasted both chunks in the correct order with no missing or duplicated tags. The file should start with <?xml and end with </project>.
  • If you see "Could not resolve dependencies", check your internet connection. Maven needs to download the libraries from the central repository.
  • Right-click the project in Package Explorer, click Maven, then click Update Project to force a refresh.

Why is my pom.xml showing errors after saving?

✔️ Awesome, I've got everything!

Great. Double check you have saved pom.xml and the dependency download has finished.

ⓧ I'd like to double check the full code

Scroll up and confirm you have both chunks pasted in order. Your pom.xml should contain:

  • The XML header, project coordinates (com.epublishing, epublishing-tests), and a <properties> block with three version variables.
  • A <dependencies> block with four dependencies: cucumber-java, cucumber-testng, testng, and selenium-java.
  • A <build> block with the maven-surefire-plugin at version 3.5.2.
  • A closing </project> tag as the very last line.
Create the Cucumber directory structure

Cucumber expects a specific folder layout. Step definitions, Page Objects, and the test runner each live in their own package under src/test/java. Feature files go under src/test/resources.

  • Right-click the src/test/java folder in Package Explorer.
  • Click New.
  • Click Package.
  • Enter com.epublishing.stepdefs as the package name.
  • Click Finish.

The new package appears in the Package Explorer. Now repeat this for the other two packages.

  • Create a second package named com.epublishing.pages using the same steps (right-click src/test/java > New > Package).
  • Create a third package named com.epublishing.runner using the same steps.

You should now see three packages under src/test/java in the Package Explorer. The last folder to create is for your Gherkin feature files.

  • Right-click the src/test/resources folder in Package Explorer.
  • Click New.
  • Click Folder.
  • Enter features as the folder name.
  • Click Finish.

Your Package Explorer should now show the full Cucumber project structure: three packages under src/test/java and a features folder under src/test/resources.

Don't see src/test/resources?

If this folder is missing, right-click the project name epublishing-tests, click New, then Source Folder, and enter src/test/resources as the path. Then create the features folder inside it.

Why is my src/test/resources folder missing in Eclipse?

Write the feature spec and verify the build

Your prompts in the next steps will need a detailed feature specification to work against. This document describes the book search feature in plain English, covering the search criteria, filters, sorting, pagination, and error states.

  • Right-click the epublishing-tests project root in Package Explorer.
  • Click New.
  • Click File.
  • Enter feature-spec.txt as the file name.
  • Click Finish.
  • Paste the following feature specification into the new file:
Book Search Feature Specification
==================================

The e-publishing site allows users to search for books using the following criteria:
- Title: partial or full title match
- Author: partial or full author name match
- ISBN: exact 13-digit ISBN match (format: 978-XXXXXXXXXX)
- Genre: select from predefined genre categories

Search Filters:
- Price range: minimum and maximum price in USD
- Publication date: start and end date range
- Language: select one or more languages
- Format: eBook, paperback, hardcover, or audiobook

Sorting Options:
- Relevance (default)
- Price (low to high, high to low)
- Publication date (newest first, oldest first)
- Average rating (highest first)

Pagination:
- 20 results displayed per page
- Navigation controls for next, previous, and page number selection

Error States:
- No results found: display a friendly message with search suggestions
- Invalid ISBN format: display validation error before executing search
- Server timeout: display error message and offer retry option after 30 seconds

Why write a detailed spec?

The quality of your prompt output is directly tied to the quality of your input. A vague spec produces vague test cases. This spec includes specific details like the ISBN format, pagination count of 20, and the 30-second timeout because those details give the LLM concrete boundaries to test against.

In the next step, you will see exactly how much the spec's detail level affects what the LLM generates.

  • Save feature-spec.txt by pressing Cmd+S (macOS) or Ctrl+S (Windows).

Now it is time to verify that everything compiles. Before you run the build, predict what you expect to see. Do you think Maven will report any errors, or should the build succeed?

  • Open the Terminal view in Eclipse by clicking Window in the menu bar, then Show View, then Terminal.
  • Navigate to your project directory by running:
cd ~/eclipse-workspace/epublishing-tests
  • Verify the project builds by running:
mvn compile

Maven downloads all four dependencies and compiles the project. You should see BUILD SUCCESS in the terminal output.

Seeing BUILD FAILURE?

  • If you see a message about missing dependencies, check that the version numbers in your <properties> block match exactly: 7.26.0 for Cucumber, 4.36.0 for Selenium, and 7.12.0 for TestNG.
  • If you see an XML parsing error, verify your pom.xml has valid XML structure. Every opening tag needs a matching closing tag.
  • If you see "No compiler is provided in this environment", make sure your Eclipse workspace is configured with a JDK (not just a JRE). Go to Window > Preferences > Java > Installed JREs and confirm a JDK is selected.

Why is my Maven build failing with these dependencies?

Your project is set up and compiling. Next up, you will open Claude.ai and experience firsthand what happens when you ask an LLM to generate test cases with a vague, unstructured prompt.

Try a Naive Prompt and See It Fall Short

Your Maven project compiles and your feature spec is ready. You have everything you need to start generating test cases with an LLM.

Before learning specific prompt engineering techniques, you need to experience the problem firsthand. What happens when you hand a feature spec to an LLM with zero guidance? The answer reveals exactly why prompt engineering matters for QA work.

In this step, get ready to:
  • Submit a naive prompt to Claude.ai and review the generated test cases.
  • Attempt to improve the output with vague additions and observe the limits of that approach.
  • Identify the specific coverage gaps that prompt engineering will address in the next step.
Submit your feature spec with a naive prompt

The simplest way to use an LLM for test generation is to paste your spec and ask for test cases. No structure, no role, no constraints.

  • Switch to Claude.ai in your browser.
  • Start a new conversation.
  • In Eclipse, open feature-spec.txt from your project root and copy its full contents.
  • Paste the feature spec into the Claude.ai message box.
  • Below your pasted spec, add this prompt:
Generate test cases for this feature.

What does this prompt tell the LLM?

Almost nothing. It gives no role ("who" should generate these tests), no output format (table, list, numbered), no coverage requirements (error states, boundaries, security), and no constraints (don't invent features).

The LLM is left to guess what "test cases" means, how thorough to be, and what format to use. That guesswork is exactly what produces shallow output.

Before you send the message, take a moment to predict: how many test cases do you think Claude will generate? Do you expect it to cover error states, boundary conditions, and security scenarios?

  • Press Enter (or click the send button) to submit your message.

Review Claude's response carefully. You will likely see a handful of test cases that mostly cover the happy path. Searches by title, author, and ISBN probably appear, but look at what is missing.

  • Count the total number of test cases Claude generated.
  • Check how many cover error states (no results found, server timeout, invalid ISBN).
  • Look for boundary condition tests (empty search, single character, maximum-length input, pagination at exactly 20 results).
  • Look for security-related tests (special characters, SQL injection attempts).
  • Notice whether the test cases have a consistent format with clear pass/fail criteria, or if they vary in structure.

Most or all of the generated test cases are happy-path scenarios. Edge cases, negative scenarios, and security tests are absent or superficial. This is the gap that vague prompts create.

Now save this output so you can compare it against the engineered version later.

  • Select and copy Claude's full response.
  • Switch back to Eclipse.
  • Right-click the epublishing-tests project in the Package Explorer.
  • Select New, then File.
  • Name the file naive-test-cases.txt and click Finish.
  • Paste Claude's output into the new file and save it with Cmd+S (macOS) or Ctrl+S (Windows).

You should see naive-test-cases.txt appear in the Package Explorer alongside feature-spec.txt and pom.xml in the project root.

Don't see the new file in Package Explorer?

  • Make sure you right-clicked the project name itself, not a subfolder. The file should be created at the project root level.
  • Try right-clicking the project and selecting Refresh (or press F5) to update the file tree.
  • If Eclipse opened a different editor view, switch to Package Explorer via Window > Show View > Package Explorer.

Why can't I see my naive-test-cases.txt file in Eclipse?

Try to improve with vague additions

A natural instinct is to ask the LLM to "try harder." You might add words like "thorough" or "edge cases" and expect a dramatically better result. Let's test that assumption.

  • Switch back to Claude.ai.
  • Start a new conversation.
  • Paste your feature spec again, then add this modified prompt below it:
Generate test cases for this feature. Be thorough and include edge cases.
  • Send the message and wait for Claude's response.

Compare this output to your first attempt. The result may be slightly longer, but look closely at the quality. The test cases are still unstructured, missing a consistent format, and lack clear pass/fail criteria.

  • Check whether the new output uses a consistent format for every test case (unique ID, preconditions, steps, expected result, priority).
  • Look for boundary tests on the search field: empty input, single character, maximum length (255 characters).
  • Look for security tests: SQL injection strings, XSS attempts, Unicode characters.
  • Check for pagination boundary tests: exactly 20 results, 21 results triggering a second page, zero results.

Why don't vague instructions fix the problem?

"Be thorough" and "include edge cases" sound helpful, but they give the LLM no concrete direction. The model does not know what YOUR definition of thorough is, which categories of test cases matter for your project, or what output format your team expects.

The output looks comprehensive at first glance but falls apart under scrutiny. This is the core problem that prompt engineering solves. Specific techniques like role-scoping, structured output formats, and explicit coverage categories give the LLM the constraints it needs to produce production-quality results.

You have now seen the problem from both angles. A vague prompt produces shallow, happy-path test cases. Adding vague qualifiers like "be thorough" barely moves the needle. The missing ingredients are specific: a defined role, an explicit output format, concrete coverage categories, and negative instructions that prevent common LLM shortcuts.

In the next step, you will apply exactly those four techniques and watch the output transform from generic to production-ready.

Engineer Prompts for Comprehensive Manual Test Cases

In Step 2, you saw the problem firsthand. A vague prompt like "generate test cases for this feature" produced shallow, happy-path-only results with no structure, no edge cases, and no clear pass/fail criteria.

The fix is four specific prompt engineering techniques: role-scoping, structured output, explicit coverage categories, and negative instructions. In this step, you will apply all four to the same feature spec and see the output transform from generic to production-ready.

In this step, get ready to:
  • Craft a role-scoped prompt with a structured output format and explicit coverage categories.
  • Add negative instructions and boundary test requirements to prevent common LLM failures.
  • Compare the engineered output against your naive results from Step 2.
Craft a role-scoped prompt with structured output

The naive prompt gave the LLM no context about who it should be or how to format its answer. Role-scoping tells the LLM to respond as a specific expert, which focuses its output on domain-relevant concerns. Structured output locks the format so every test case comes out consistent and comparable.

  • Switch back to Claude.ai in your browser.
  • Start a new conversation so the naive prompt's context does not bleed in.
  • Open feature-spec.txt in Eclipse and copy the full contents of the file.
  • Paste the following prompt into Claude.ai, replacing the placeholder at the bottom with your copied feature spec:
You are a senior QA engineer with 10 years of experience testing e-commerce web applications.

I will provide you with a feature specification for a book search feature on an e-publishing website. Generate comprehensive manual test cases in a table with these columns: Test Case ID, Category, Precondition, Test Steps, Expected Result, Priority.

Generate test cases for each of these categories:
- Positive/happy path
- Negative/error handling
- Boundary conditions
- Security/injection
- Usability
- Performance edge cases

Here is the feature specification:

[[FEATURE_SPEC="paste the full contents of your feature-spec.txt here"]]

What do these techniques do?

Role-scoping ("You are a senior QA engineer...") primes the LLM to think like a tester, not a generalist. It draws on patterns the model learned from QA-specific training data, so the output includes concerns a tester would raise.

Structured output (the table columns) eliminates vague, paragraph-style test cases. Every test case gets the same fields, making them easy to review and track.

Explicit coverage categories force the LLM to produce tests it would otherwise skip. Without them, the model defaults to happy-path scenarios because those are the most common in its training data.

Add negative instructions and boundary coverage

LLMs tend to invent features that sound plausible but are not in the spec. They also gloss over boundary conditions unless you list them explicitly. Negative instructions act as guardrails that prevent these failure modes.

  • Before sending, add the following text to the end of your prompt (before the feature spec), right after the list of categories:
Do not invent features not described in the spec.
Do not assume any default behavior that is not explicitly stated.
If the spec is ambiguous about a behavior, flag it as an assumption and create test cases for both possible interpretations.

For the search field, include boundary tests for:
- Empty input
- Single character
- Maximum length (255 characters)
- Special characters
- SQL injection strings
- Unicode characters

What do negative instructions prevent?

"Do not invent features" stops the LLM from generating test cases for functionality your spec never mentioned. Without this, you will often see test cases for features like "user login" or "shopping cart" that feel plausible but do not exist in the actual spec.

The explicit boundary list ensures coverage of inputs like empty strings, max-length values, and injection attacks. These are the exact tests that the naive prompt missed entirely.

Send the engineered prompt and compare results

Your full prompt now has all four techniques stacked together. Before you send it, take a moment to predict what will be different this time.

Predict before you send

How many test cases do you think the engineered prompt will produce compared to the naive one? Which new categories do you expect to see? Hold your prediction in mind, then send the prompt.

  • Verify your full prompt in Claude.ai has three sections in order: the role definition and output format, the negative instructions and boundary list, and the pasted feature spec at the bottom.
  • Press Enter or click the send button to submit the prompt.

Claude responds with a structured table of test cases. Scroll through the full output and count how many test cases it produced.

  • Check that the output contains 20 or more test cases organized by the six categories you specified.
  • Verify each test case has all six columns: Test Case ID, Category, Precondition, Test Steps, Expected Result, and Priority.
  • Look for specific boundary tests: empty search input, single-character input, 255-character maximum length, special characters, SQL injection strings, and Unicode characters.

What should I see?

The output should be dramatically different from the naive version. You should see a formatted table with unique IDs like TC-001 through TC-025 or more. Each row maps to one of your six categories. Security tests now include SQL injection and XSS inputs. Boundary tests cover the exact inputs you listed.

If the spec was ambiguous about any behavior (for example, what happens when the search field exceeds 255 characters), you should also see flagged assumptions with test cases for both possible interpretations.

  • Open your naive-test-cases.txt file in Eclipse and compare it side by side with the Claude output.
  • Count how many test cases the naive prompt produced versus the engineered prompt.
  • Note the categories that are entirely missing from the naive output but present in the engineered output (security, boundary, and performance edge cases are typically absent).
  • Select all of the Claude output and copy it.
  • In Eclipse, create a new file called engineered-test-cases.txt in the epublishing-tests project root (the same folder where feature-spec.txt and naive-test-cases.txt live).
  • Paste the output and save the file.

Seeing fewer than 20 test cases?

If Claude's output is shorter than expected, check that your prompt includes all six coverage categories and the boundary test list. A missing category means fewer tests generated for that area.

If the output was cut off mid-table, type "continue" in the chat to have Claude finish generating the remaining test cases. Copy both parts into your saved file.

Why is my engineered prompt still producing shallow results?

You now have a reusable prompt template that turns any feature spec into thorough, structured manual test cases. Next up, you will chain this output into a new prompt that converts these test cases into executable Cucumber/Gherkin scenarios.

Generate Cucumber Gherkin Scenarios with Prompt Chaining

Your engineered prompt produced structured, comprehensive manual test cases. That is a massive improvement over the naive version, but your team uses Cucumber and Gherkin for executable specs. Converting 20+ manual test cases into Gherkin by hand would be tedious and error-prone.

Instead, you will chain prompts: feed the manual test cases as context into a new prompt that generates Gherkin. You will also use few-shot prompting to show the LLM exactly the output style you want before it generates anything.

In this step, get ready to:
  • Build a prompt chain that uses your manual test cases as context for Gherkin generation.
  • Add constraints and a few-shot example to control the output format and style.
  • Save and verify the generated feature file in Eclipse.
Start a new conversation with your test cases as context

Prompt chaining is when the output of one prompt becomes the input for the next. Your engineered test cases from Step 3 are the output of one prompt engineering cycle. Now they become the context for a new prompt that generates Gherkin.

  • Switch back to Claude.ai in your browser.
  • Start a new conversation by clicking the compose icon or the New chat button.
  • Open engineered-test-cases.txt in Eclipse and copy its full contents.
  • Paste the test cases into the Claude.ai message box.
  • Below the pasted test cases, add the following prompt text with a few-shot example:
Convert these manual test cases into Cucumber Gherkin
feature file scenarios for the book search feature.

Here is an example of the declarative Gherkin style
I want you to follow:

Scenario: User views book details
  Given the user is on the book listing page
  When the user selects a book from the results
  Then the book detail page is displayed
  And the book title, author, and price are shown

Notice the steps describe WHAT happens (behavior),
not HOW it happens (no UI interactions like
"click the button" or "enter text in the field").

What is few-shot prompting?

Few-shot prompting is providing the LLM with concrete examples of the output format you want before asking it to generate. Instead of describing the style in abstract terms, you show it.

The example scenario above demonstrates declarative style. Declarative steps describe behavior ("search results are displayed") rather than implementation ("user clicks the Search button and waits for the results div to load"). Cucumber's official docs recommend declarative style because it is less brittle when the UI changes.

Add constraints and review the output

The few-shot example shows the style, but the LLM still needs explicit structural constraints. Without them, it might skip grouping, omit shared preconditions, or produce overly long scenarios.

  • Add the following constraints below the few-shot example in the same message (do not send yet):
Follow these constraints:
- Use the Feature keyword with a meaningful description
  in "As a [role], I want [goal], So that [benefit]" format
- Group related scenarios under Rule keywords
- Use Background for shared preconditions that apply
  to all scenarios
- Write all steps in declarative style describing behavior,
  not imperative style describing UI interactions
- Each scenario should have 3-5 steps maximum
- Use Given for preconditions, When for actions,
  Then and And for expected outcomes

What do these constraints do?

  • The Feature keyword with an "As a... I want... So that..." description forces the LLM to frame the feature from the user's perspective.
  • The Rule keyword groups related scenarios under a business rule, making the feature file scannable.
  • The Background keyword extracts shared preconditions so they are not repeated in every scenario.
  • The 3-5 step maximum prevents the LLM from generating sprawling scenarios that are hard to debug when they fail.
  • Send the full message (pasted test cases + few-shot example + constraints) to Claude.

Claude generates a complete Gherkin feature file. Review the output before you copy anything.

  • Check that the output starts with Feature: followed by an "As a... I want... So that..." description.
  • Check that a Background: section exists with the shared precondition (the user being on the search page).
  • Check that scenarios are grouped under Rule: keywords separating happy-path scenarios from error-handling scenarios.
  • Confirm that each scenario has 3-5 steps and uses declarative language (describes behavior, not button clicks).
  • Verify the scenarios cover the key test cases from your manual test cases: title search, author search, ISBN search, empty input, no results, and special character handling.

If scenarios are missing or in imperative style

If Claude produced imperative steps like "user clicks the Search button" or missed coverage categories, send a follow-up message in the same conversation. Reference the specific issue: "The empty input scenario uses imperative steps. Rewrite it in declarative style as shown in my example." Prompt chaining works across multiple messages in a single conversation.

Save and verify the feature file in Eclipse

The generated Gherkin needs to live in your project's feature file directory so Cucumber can find it. You will also verify the syntax is correct by reviewing it in Eclipse.

  • Copy the full generated Gherkin output from Claude (the feature file content only, not any surrounding explanation).
  • Switch back to Eclipse.
  • Right-click on the src/test/resources/features/ folder in the Package Explorer panel.
  • Select New, then File.
  • Name the file book_search.feature and click Finish.
  • Paste the copied Gherkin content into the new file.
  • Save the file with Cmd+S (macOS) or Ctrl+S (Windows).

Before you review the file, think about what Gherkin keywords you expect to see. Which keywords should appear at the top level? Which keywords should repeat for each test scenario?

Open book_search.feature in Eclipse and review it. You should see a single Feature: declaration at the top, one Background: section, at least two Rule: groups, and multiple Scenario: blocks using Given, When, Then, and And steps in declarative style.

What should the file structure look like?

Your feature file should follow this structure: Feature (with user-story description) > Background (shared precondition) > Rule (grouping related scenarios) > Scenario (with Given/When/Then steps). Each scenario should map back to one or more manual test cases from Step 3.

The scenarios should cover at least: searching by title, searching by author, searching by ISBN, submitting empty input, getting no results, and handling special characters.

✔️ My feature file has all the right keywords and scenarios

Your prompt chain produced valid Gherkin. Double check that the file is saved and that you can see it in the src/test/resources/features/ directory in Eclipse's Package Explorer.

ⓧ My output is missing keywords or scenarios

Go back to your Claude.ai conversation and send a follow-up message that names the specific gap. For example: "Add a Background section with the shared precondition" or "Group the scenarios under Rule keywords." Prompt chaining works across messages in the same conversation, so Claude still has your test cases as context.

If the output is significantly off, start a new conversation and re-send the full prompt (test cases + few-shot example + constraints) in one message. Copy the corrected Gherkin into your book_search.feature file and save again.

Seeing syntax highlighting errors in Eclipse?

Eclipse may not have a Gherkin syntax plugin installed by default. The feature file can still be used by Cucumber even without syntax highlighting. Check that the file extension is .feature (not .feature.txt or .gherkin).

Also verify that the file is inside src/test/resources/features/ and not in the project root or another directory.

help me troubleshoot my Gherkin feature file in Eclipse

You now have a valid Gherkin feature file generated entirely through engineered prompts. Next up, you will chain one more prompt to generate the Java step definitions and Page Object class that make these scenarios executable.

Generate Java Step Definitions and Validate

Your book_search.feature file describes what the system should do in plain English. To actually execute those scenarios, Cucumber needs Java step definitions that map each Given/When/Then line to real code.

Writing all those step definitions by hand would be tedious. Instead, you will chain another prompt in Claude.ai, feeding your Gherkin as context and constraining the output with specific Java conventions. Then you will paste the generated code into Eclipse and let the compiler catch any hallucinated imports or APIs.

In this step, get ready to:
  • Craft a chained prompt with Gherkin context and specific Java constraints.
  • Add a few-shot example and review Claude's generated code for hallucinations.
  • Create the Java files in Eclipse and validate with mvn compile.
Craft the step definition prompt with constraints

In Step 4, you gave Claude your manual test cases and got Gherkin back. Now you will give Claude the Gherkin and ask for Java code. The key is specificity: you need to tell Claude exactly which packages, annotation styles, and assertion libraries to use.

  • Switch to Claude.ai in your browser.
  • Start a new conversation.
  • Copy the full contents of your book_search.feature file from Eclipse.
  • Paste it into Claude.ai along with the following prompt (paste the Gherkin where the prompt says to):
I have a Cucumber Gherkin feature file for a book search feature
on an e-publishing site. Here it is:

[Paste your book_search.feature content here]

Generate the following Java files for this feature file:

1. A step definitions class (BookSearchStepDefs.java) in package
   com.epublishing.stepdefs
   - Use annotations: io.cucumber.java.en.Given,
     io.cucumber.java.en.When, io.cucumber.java.en.Then,
     io.cucumber.java.en.And
   - Use TestNG assertions (org.testng.Assert), not JUnit
   - Initialize ChromeDriver in the @Given setup step

2. A Page Object class (BookSearchPage.java) in package
   com.epublishing.pages
   - Use Selenium WebDriver By locators with
     driver.findElement(By.id(...)) style
   - Use WebDriverWait with Duration for explicit waits
   - Do not use any deprecated Selenium 4.x APIs
   - Do not invent any Selenium API methods that do not exist

3. A TestNG runner class (TestRunner.java) in package
   com.epublishing.runner
   - Extend AbstractTestNGCucumberTests
   - Use @CucumberOptions with features and glue paths

What do these constraints do?

Each constraint targets a common LLM failure mode. Specifying io.cucumber.java.en.Given prevents Claude from hallucinating old cucumber.api.java.en imports that no longer exist in Cucumber 7.x.

Saying "do not invent any Selenium API methods that do not exist" is a negative instruction. It forces the model to stick to real Selenium 4.x APIs instead of generating plausible-looking method names that would cause compilation errors.

Add a few-shot example and review the output

A few-shot example shows Claude exactly the code style and structure you expect. Without it, the model guesses at constructor patterns, field naming, and how you want WebDriver injected.

  • Before sending your prompt, add the following few-shot example at the end:
Here is an example of the step definition style I want:

package com.epublishing.stepdefs;

import com.epublishing.pages.BookSearchPage;
import io.cucumber.java.en.Given;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

public class BookSearchStepDefs {
    private WebDriver driver;
    private BookSearchPage bookSearchPage;

    @Given("the user is on the book search page")
    public void theUserIsOnTheBookSearchPage() {
        driver = new ChromeDriver();
        bookSearchPage = new BookSearchPage(driver);
        bookSearchPage.navigateToSearchPage();
    }
}

Follow this exact style for all step definitions.

Why include a few-shot example?

The example anchors Claude on your exact conventions. It shows that WebDriver is a class field (not a parameter), that BookSearchPage takes a WebDriver in its constructor, and that step method names follow the camelCase-from-Gherkin pattern.

Without this example, Claude might create a completely different structure that compiles but does not match your project's conventions.

  • Send the full prompt (Gherkin + constraints + few-shot example) to Claude.
  • Review the generated code carefully before copying anything.

Check the output for these specific issues:

  • Do the import statements use io.cucumber.java.en (correct) or cucumber.api.java.en (hallucinated, deprecated)?
  • Does the BookSearchPage class use By.id() and By.cssSelector() locators, or invented method names?
  • Does the runner class extend AbstractTestNGCucumberTests (correct) or a JUnit runner (wrong framework)?
  • Does the WebDriverWait constructor use Duration.ofSeconds() (current API) or a raw long (deprecated)?

Common hallucination patterns

LLMs frequently invent Selenium helper methods like waitForElement() or clickAndWait() that look real but do not exist in the Selenium API. They also mix up TestNG and JUnit assertion imports. Flag any method call you do not recognize.

Create the Java files in Eclipse and compile

Now you will create the three Java files in your Eclipse project and paste Claude's generated code into them. Then the compiler will catch anything the LLM got wrong.

  • Switch back to Eclipse.
  • In the **Package Explorer**, right-click the com.epublishing.runner package under src/test/java.
  • Select **New**, then **Class**.
  • Type TestRunner in the **Name** field.
  • Click **Finish**.
  • Replace the auto-generated code with Claude's TestRunner class. It should look like this:
package com.epublishing.runner;

import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;

@CucumberOptions(
        features = "src/test/resources/features",
        glue = "com.epublishing.stepdefs",
        plugin = {"pretty", "html:target/cucumber-reports.html"}
)
public class TestRunner extends AbstractTestNGCucumberTests {
}

What does this class do?

The TestRunner class is the glue between Cucumber and TestNG. The @CucumberOptions annotation tells Cucumber where to find your .feature files and step definitions.

Extending AbstractTestNGCucumberTests lets TestNG discover and run your Cucumber scenarios as test methods.

  • Save TestRunner.java with **Ctrl+S** (Windows) or **Cmd+S** (macOS).

Now create the Page Object class the same way.

  • Right-click the com.epublishing.pages package in the **Package Explorer**.
  • Select **New**, then **Class**.
  • Type BookSearchPage in the **Name** field and click **Finish**.
  • Replace the auto-generated code with Claude's BookSearchPage class.
  • Save the file.

What should BookSearchPage contain?

This class follows the Page Object Model pattern. It wraps all the web element locators and actions for the search page into one class. Your step definitions call methods like searchFor() and areResultsDisplayed() instead of repeating driver.findElement() everywhere.

Check that the constructor takes a WebDriver parameter and initializes a WebDriverWait with Duration.ofSeconds(10). If Claude used a raw long value instead of Duration, that is a hallucination of the deprecated API. Fix it.

You should now see TestRunner.java and BookSearchPage.java in their respective packages in the **Package Explorer**.

  • Right-click the com.epublishing.stepdefs package in the **Package Explorer**.
  • Select **New**, then **Class**.
  • Type BookSearchStepDefs in the **Name** field and click **Finish**.
  • Replace the auto-generated code with Claude's BookSearchStepDefs class.
  • Save the file.

What should BookSearchStepDefs contain?

Each @Given, @When, @Then, and @And annotation should contain the exact Gherkin step text from your feature file. For example, @When("the user searches for {string}") matches the scenario step When the user searches for "The Great Gatsby".

Verify that assertions use org.testng.Assert, not org.junit.Assert. Mixing assertion libraries is one of the most common LLM hallucinations in testing projects.

All three files are now in your project. Time to see if the generated code actually compiles.

  • Before you run the compile, predict: do you think Claude's code will compile on the first try, or will the compiler catch hallucinated APIs?
  • Open a terminal in Eclipse by clicking **Window** in the menu bar, then **Show View**, then **Terminal**.
  • Verify the compilation by running this command:
mvn compile

✔️ BUILD SUCCESS

The compiler found no errors in the generated code. All three Java files use valid imports, correct annotations, and real Selenium API methods. This is the best-case outcome of well-constrained prompts.

  • Note which constraints you think made the biggest difference (the negative instructions, the annotation specificity, or the few-shot example).

ⓧ BUILD FAILURE with compilation errors

Compilation errors mean Claude hallucinated something. This is expected and is actually a valuable learning moment.

  • Read the error messages in the terminal output. Each one tells you the exact file, line number, and what went wrong.
  • Common fixes:
  • Fix each error, save the files, and run mvn compile again.
  • Repeat until you see BUILD SUCCESS.

Still seeing errors after fixing?

If your error mentions a missing class like NoAlertPresentException, make sure the import uses the full path org.openqa.selenium.NoAlertPresentException. Claude sometimes omits imports for exception classes.

If you are stuck, help me fix the compilation errors in my Cucumber step definitions.

What did the compiler just prove?

A successful mvn compile confirms that every import path is real, every method call exists in the library APIs, and every annotation is correctly formed. The compiler is your safety net against LLM hallucinations.

This is why you validate generated code in a real project instead of trusting LLM output at face value. Even well-constrained prompts can produce code that looks correct but fails to compile.

✔️ Awesome, I've got everything!

Great. Double check you have saved all three files.

ⓧ I'd like to double check the full code

Here is the reference code for TestRunner.java. If Claude's output differs, use this version:

package com.epublishing.runner;

import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;

@CucumberOptions(
        features = "src/test/resources/features",
        glue = "com.epublishing.stepdefs",
        plugin = {"pretty", "html:target/cucumber-reports.html"}
)
public class TestRunner extends AbstractTestNGCucumberTests {
}

For BookSearchPage.java, confirm the following pieces are present and in order:

  • Package declaration: package com.epublishing.pages;.
  • Imports from org.openqa.selenium (By, WebDriver, WebElement) and org.openqa.selenium.support.ui (WebDriverWait, ExpectedConditions).
  • Six By locator fields: searchInput, searchButton, searchResults, noResultsMessage, validationMessage, searchSuggestions.
  • A constructor that takes WebDriver and initializes WebDriverWait with Duration.ofSeconds(10).
  • Methods: navigateToSearchPage(), searchFor(), submitEmptySearch(), areResultsDisplayed(), getSearchResults(), getNoResultsMessage(), getValidationMessage(), areSuggestionsDisplayed().

For BookSearchStepDefs.java, confirm these pieces:

  • Package declaration: package com.epublishing.stepdefs;.
  • Imports from io.cucumber.java.en (Given, When, Then, And) and org.testng.Assert.
  • A @Given method that creates a ChromeDriver and initializes BookSearchPage.
  • Separate @When methods for general search, author search, ISBN search, and empty-input search.
  • A noScriptExecutionOccurs() method that catches NoAlertPresentException to verify no XSS occurred.

Your prompt-generated Java code compiles cleanly against the real Cucumber, Selenium, and TestNG libraries. Next up, you will put all the techniques from this project to the test in the Secret Mission.

Secret mission

Generate Data-Driven Scenario Outlines

Real-world test suites need data-driven tests to cover multiple input combinations without duplicating scenarios. In this mission, you will combine all four prompt engineering techniques into a single prompt that generates Cucumber Scenario Outlines with parameterized test data. The challenge is producing both valid Gherkin syntax and meaningful test data spanning valid searches, edge cases, and error conditions.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally and uses Claude.ai's free tier, so there are no ongoing costs. Decide whether to keep your project files for future reference, or delete them entirely.

Resources you used:

  • epublishing-tests Maven project in your Eclipse workspace (includes all Java source files, Gherkin feature files, and text files like feature-spec.txt, naive-test-cases.txt, and engineered-test-cases.txt).
  • Claude.ai conversation history (free tier, no ongoing costs).

Keep everything running

No action needed. Choose this if you want to keep your project as a reusable template for future prompt-engineered test suites.

  • Your epublishing-tests project stays in your Eclipse workspace, ready to open anytime.
  • Consider saving your best-performing prompts from Claude.ai into a prompts/ folder in the project root so you can reuse them across future projects.
  • Claude.ai is free and has no ongoing charges. Your conversations remain accessible in your history.

Pause - I'll come back to this later

There are no running processes or paid services to pause. Everything in this project is local files and a free-tier web tool.

  • Close Eclipse if you want to free up memory. Your project is saved automatically and will reappear when you reopen Eclipse.
  • Your Claude.ai conversations persist in your account history for reference later.

Delete - I don't want to use this again

Remove all project files and start fresh.

  • In Eclipse, right-click the epublishing-tests project in the Package Explorer.
  • Select Delete.
  • In the confirmation dialog, check the Delete project contents on disk checkbox. This removes the files from your computer, not just from Eclipse.
  • Click OK to confirm.
  • To clear your Claude.ai conversations, open your conversation history in the sidebar. Hover over each conversation from this project and click the delete icon to remove it.

Nice Work!

Nice Work!

Nice work! You just built a complete test suite for an e-publishing site's book search feature, and every piece of it was generated through engineered prompts in Claude.ai.

What you learned:

  • Experienced the failure mode of naive prompting firsthand, then applied four core techniques (role-scoping, structured output, negative instructions, and explicit coverage requirements) to transform shallow output into 20+ comprehensive manual test cases.
  • Used prompt chaining and few-shot examples to generate declarative-style Cucumber/Gherkin scenarios that map directly back to your manual test cases.
  • Generated Java step definitions and a Page Object class through engineered prompts and validated they compile in your real Maven project with mvn compile.
  • Secret Mission: Engineered prompts for data-driven Scenario Outlines with parameterized test data covering edge cases like SQL injection, max-length input, and empty searches.

Ready to quiz yourself?