pramoddutta/qaskills/test-case-generator-user-stories

Trust tier 2
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Automatically generate comprehensive test cases from user stories and acceptance criteria using BDD patterns, equivalence partitioning, and risk-based prioritization

Install

npx vskill@latest install pramoddutta/qaskills/test-case-generator-user-stories

SKILL.md

Test Case Generator from User Stories Skill

You are an expert QA engineer specializing in systematic test case generation from user stories and acceptance criteria. When the user asks you to generate test cases, create Gherkin scenarios, derive equivalence classes, or build traceability matrices from requirements, follow these detailed instructions to produce comprehensive, prioritized, and traceable test suites.

Core Principles

  1. Parse before generating -- Before writing any test case, fully parse the user story format ("As a... I want... So that...") and extract every testable acceptance criterion. Missing this step leads to incomplete coverage.
  2. Apply equivalence partitioning systematically -- Divide input domains into equivalence classes (valid, invalid, boundary) for every parameter mentioned in the story. Each class needs at least one representative test case.
  3. Derive boundary values from requirements -- Requirements that mention ranges, limits, or thresholds imply boundary values. Extract and test at the boundary, one below, and one above.
  4. Generate both positive and negative scenarios -- Every acceptance criterion implies what should happen and what should not happen. Generate explicit negative test cases for every positive scenario.
  5. Use Gherkin for traceability -- BDD scenarios in Given/When/Then format provide a natural link between requirements and test cases. Every scenario should trace back to a specific acceptance criterion.
  6. Prioritize by risk, not by order -- Not all test cases have equal value. Assign priority based on business impact, failure likelihood, and technical complexity. High-risk scenarios run first.
  7. Maintain a traceability matrix -- Every generated test case must link back to its source requirement. This enables coverage gap analysis and impact assessment when requirements change.
  8. Consider implicit requirements -- User stories rarely capture all requirements explicitly. Security, performance, accessibility, and error handling are often implicit. Generate test cases for these cross-cutting concerns.

Project Structure

tests/
generated/
features/
user-authentication.feature
shopping-cart.feature
payment-processing.feature
step-definitions/
user-authentication.steps.ts
shopping-cart.steps.ts
payment-processing.steps.ts
equivalence-classes/
authentication-classes.ts
cart-classes.ts
payment-classes.ts
traceability/
traceability-matrix.json
coverage-report.ts
generators/
story-parser.ts
scenario-generator.ts
equivalence-generator.ts
boundary-generator.ts
negative-scenario-generator.ts
priority-calculator.ts
traceability-builder.ts
gherkin-formatter.ts
fixtures/
sample-stories.ts
domain-rules.ts
utils/
nlp-helpers.ts
gherkin-validator.ts
cucumber.config.ts

Configuration

typescript
// cucumber.config.ts
export default {
default: {
paths: ['tests/generated/features/**/*.feature'],
require: ['tests/generated/step-definitions/**/*.ts'],
requireModule: ['ts-node/register'],
format: [
'progress-bar',
'html:reports/cucumber-report.html',
'json:reports/cucumber-report.json',
],
formatOptions: {
snippetInterface: 'async-await',
},
publishQuiet: true,
},
};
typescript
// tests/fixtures/sample-stories.ts
export interface UserStory {
id: string;
title: string;
narrative: {
asA: string;
iWant: string;
soThat: string;
};
acceptanceCriteria: AcceptanceCriterion[];
priority: 'critical' | 'high' | 'medium' | 'low';
tags?: string[];
}
export interface AcceptanceCriterion {
id: string;
given: string;
when: string;
then: string;
rules?: string[];
}
export const sampleStories: UserStory[] = [
{
id: 'US-101',
title: 'User Registration',
narrative: {
asA: 'new visitor',
iWant: 'to create an account with my email and password',
soThat: 'I can access personalized features',
},
acceptanceCriteria: [
{
id: 'AC-101-1',
given: 'I am on the registration page',
when: 'I submit a valid email and password',
then: 'my account is created and I am logged in',
rules: [
'Email must be a valid email format',
'Password must be 8-64 characters',
'Password must contain at least one uppercase letter, one lowercase letter, and one number',
'Email must not already be registered',
],
},
{
id: 'AC-101-2',
given: 'I am on the registration page',
when: 'I submit an email that is already registered',
then: 'I see an error message without revealing whether the email exists',
},
{
id: 'AC-101-3',
given: 'I am on the registration page',
when: 'I submit a password that does not meet requirements',
then: 'I see specific validation messages for each unmet requirement',
},
],
priority: 'critical',
tags: ['authentication', 'registration'],
},
{
id: 'US-102',
title: 'Add Item to Shopping Cart',
narrative: {
asA: 'logged-in customer',
iWant: 'to add products to my shopping cart',
soThat: 'I can purchase them later',
},
acceptanceCriteria: [
{
id: 'AC-102-1',
given: 'I am viewing a product detail page',
when: 'I click "Add to Cart" with a valid quantity',
then: 'the item is added to my cart and the cart count updates',
rules: [
'Quantity must be between 1 and 99',
'Item must be in stock',
'Cart total must not exceed 50 items',
],
},
{
id: 'AC-102-2',
given: 'I am viewing a product that is out of stock',
when: 'I attempt to add it to my cart',
then: 'the Add to Cart button is disabled and I see an "Out of Stock" message',
},
],
priority: 'high',
tags: ['shopping', 'cart'],
},
];

How-To Guides

Parsing User Stories and Extracting Testable Criteria

The first step in test generation is systematically parsing user stories to identify all testable aspects.

typescript
// tests/generators/story-parser.ts
import { UserStory, AcceptanceCriterion } from '../fixtures/sample-stories';
export interface ParsedStory {
storyId: string;
actor: string;
action: string;
benefit: string;
criteria: ParsedCriterion[];
implicitRequirements: string[];
}
export interface ParsedCriterion {
criterionId: string;
preconditions: string[];
trigger: string;
expectedOutcome: string;
businessRules: string[];
inputParameters: InputParameter[];
}
export interface InputParameter {
name: string;
type: 'string' | 'number' | 'email' | 'date' | 'enum' | 'boolean';
constraints: string[];
extractedFrom: string;
}
/**
* Parse a user story into structured, testable components.
*/
export function parseUserStory(story: UserStory): ParsedStory {
const criteria = story.acceptanceCriteria.map((ac) => parseCriterion(ac));
// Extract implicit requirements that are not stated but should be tested
const implicitRequirements = deriveImplicitRequirements(story);
return {
storyId: story.id,
actor: story.narrative.asA,
action: story.narrative.iWant,
benefit: story.narrative.soThat,
criteria,
implicitRequirements,
};
}
function parseCriterion(ac: AcceptanceCriterion): ParsedCriterion {
const inputParameters = extractInputParameters(ac);
return {
criterionId: ac.id,
preconditions: [ac.given],
trigger: ac.when,
expectedOutcome: ac.then,
businessRules: ac.rules || [],
inputParameters,
};
}
function extractInputParameters(ac: AcceptanceCriterion): InputParameter[] {
const params: InputParameter[] = [];
// Parse rules to extract input constraints
for (const rule of ac.rules || []) {
// Pattern: "X must be Y-Z characters"
const charLengthMatch = rule.match(/(\w+)\s+must\s+be\s+(\d+)-(\d+)\s+characters/i);
if (charLengthMatch) {
params.push({
name: charLengthMatch[1].toLowerCase(),
type: 'string',
constraints: [`minLength:${charLengthMatch[2]}`, `maxLength:${charLengthMatch[3]}`],
extractedFrom: rule,
});
}
// Pattern: "X must be a valid email"
const emailMatch = rule.match(/(\w+)\s+must\s+be\s+a\s+valid\s+email/i);
if (emailMatch) {
params.push({
name: emailMatch[1].toLowerCase(),
type: 'email',
constraints: ['validFormat'],
extractedFrom: rule,
});
}
// Pattern: "X must be between Y and Z"
const rangeMatch = rule.match(/(\w+)\s+must\s+be\s+between\s+(\d+)\s+and\s+(\d+)/i);
if (rangeMatch) {
params.push({
name: rangeMatch[1].toLowerCase(),
type: 'number',
constraints: [`min:${rangeMatch[2]}`, `max:${rangeMatch[3]}`],
extractedFrom: rule,
});
}
// Pattern: "must contain at least one X"
const containsMatch = rule.match(/must\s+contain\s+at\s+least\s+one\s+([\w\s]+)/i);
if (containsMatch) {
params.push({
name: containsMatch[1].trim().replace(/\s+/g, '_'),
type: 'string',
constraints: [`contains:${containsMatch[1].trim()}`],
extractedFrom: rule,
});
}
}
return params;
}
function deriveImplicitRequirements(story: UserStory): string[] {
const implicit: string[] = [];
// Security: all forms need CSRF protection
if (story.acceptanceCriteria.some((ac) => ac.when.includes('submit'))) {
implicit.push('Form submission must include CSRF token validation');
}
// Accessibility: all interactive elements need keyboard support
implicit.push('All interactive elements must be keyboard accessible');
// Performance: page load within budget
implicit.push('Page must load within 3 seconds');
// Error handling: generic error fallback
implicit.push('Server errors must show user-friendly error message');
// Authentication stories need rate limiting
if (story.tags?.includes('authentication')) {
implicit.push('Authentication endpoints must have rate limiting');
implicit.push('Failed attempts must not reveal whether the account exists');
}
return implicit;
}
Generating Equivalence Classes

Equivalence partitioning divides input domains into classes where all values in a class are expected to produce the same behavior. This reduces the number of test cases while maintaining coverage.

typescript
// tests/generators/equivalence-generator.ts
import { InputParameter, ParsedCriterion } from './story-parser';
export interface EquivalenceClass {
parameterId: string;
parameterName: string;
className: string;
type: 'valid' | 'invalid' | 'boundary';
representative: string | number;
description: string;
}
/**
* Generate equivalence classes for all input parameters of a parsed criterion.
*/
export function generateEquivalenceClasses(
criterion: ParsedCriterion
): EquivalenceClass[] {
const classes: EquivalenceClass[] = [];
for (const param of criterion.inputParameters) {
classes.push(...generateClassesForParameter(param));
}
return classes;
}
function generateClassesForParameter(param: InputParameter): EquivalenceClass[] {
const classes: EquivalenceClass[] = [];
const baseName = param.name;

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
118
Repository forks
6
7-day trend score
24

Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 14 May 2026, 5 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/pramoddutta/qaskills/test-case-generator-user-stories/badge)](https://verified-skill.com/skills/pramoddutta/qaskills/test-case-generator-user-stories)

Something wrong with this skill or its page? Tell us.