{ "name": "Testing Automation Workflow", "description": "Comprehensive testing workflow for unit, integration, and end-to-end testing", "workflowType": "testing-automation", "applicablePatterns": ["Unit Testing", "Integration Testing", "E2E Testing", "Performance Testing"], "phases": { "planning": { "description": "Test planning and strategy phase", "activities": [ "Define testing strategy and coverage goals", "Identify critical paths and edge cases", "Plan test data and fixtures", "Define testing environments and CI/CD integration", "Establish quality gates and acceptance criteria" ] }, "implementation": { "description": "Test implementation phase", "activities": [ "Write unit tests for individual functions and components", "Create integration tests for API endpoints and workflows", "Implement end-to-end tests for user journeys", "Set up test data factories and fixtures", "Configure test environments and mocking" ] }, "automation": { "description": "Test automation and CI/CD integration", "activities": [ "Integrate tests into CI/CD pipeline", "Set up parallel test execution", "Configure test reporting and notifications", "Implement test result analysis and trending", "Set up automated test maintenance" ] }, "monitoring": { "description": "Test monitoring and maintenance phase", "activities": [ "Monitor test execution metrics and trends", "Maintain test suites and remove flaky tests", "Update tests for new features and changes", "Analyze test coverage and identify gaps", "Optimize test execution performance" ] } }, "testingLevels": { "unit": { "scope": "Individual functions, methods, and components in isolation", "goals": "Fast feedback, high coverage, isolated testing", "tools": "Jest, Vitest, Mocha, Jasmine", "coverage": "80%+ for business logic", "characteristics": "Fast (<1s), Isolated, Repeatable, Self-validating" }, "integration": { "scope": "Interaction between multiple components or services", "goals": "Verify component integration and data flow", "tools": "Supertest, TestContainers, Testing Library", "coverage": "Critical integration points", "characteristics": "Moderate speed, Real dependencies, Contract validation" }, "contract": { "scope": "API contracts between services", "goals": "Ensure API compatibility and prevent breaking changes", "tools": "Pact, OpenAPI validators, Postman", "coverage": "All public APIs", "characteristics": "Consumer-driven, Version compatibility, Schema validation" }, "e2e": { "scope": "Complete user workflows from start to finish", "goals": "Validate critical user journeys work end-to-end", "tools": "Playwright, Cypress, Selenium", "coverage": "Critical business flows", "characteristics": "Slow, Real browser, Full system testing" }, "performance": { "scope": "System performance under various load conditions", "goals": "Ensure performance requirements are met", "tools": "Artillery, K6, JMeter, Lighthouse", "coverage": "Critical performance paths", "characteristics": "Load testing, Stress testing, Performance monitoring" } }, "testingPatterns": { "aaa": { "name": "Arrange, Act, Assert", "description": "Structure tests with clear setup, execution, and verification", "example": "// Arrange\nconst user = createTestUser();\n// Act\nconst result = await service.createUser(user);\n// Assert\nexpect(result.id).toBeDefined();" }, "given_when_then": { "name": "Given, When, Then (BDD)", "description": "Behavior-driven testing with clear preconditions, actions, and outcomes", "example": "describe('User registration', () => {\n it('should create user when valid data provided', async () => {\n // Given\n const userData = validUserData();\n // When\n const user = await userService.register(userData);\n // Then\n expect(user).toMatchObject(userData);\n });\n});" }, "test_doubles": { "name": "Test Doubles (Mocks, Stubs, Spies)", "description": "Use test doubles to isolate system under test", "types": { "mock": "Verify interactions with dependencies", "stub": "Provide controlled responses", "spy": "Monitor calls to real objects", "fake": "Working implementation with shortcuts" } }, "data_driven": { "name": "Data-Driven Testing", "description": "Test same logic with multiple input datasets", "example": "test.each([\n ['valid@email.com', true],\n ['invalid-email', false],\n ['', false]\n])('validates email %s as %s', (email, expected) => {\n expect(isValidEmail(email)).toBe(expected);\n});" } }, "qualityGates": { "coverage": { "unit_tests": "80% minimum for business logic", "integration_tests": "All critical integration points covered", "e2e_tests": "All critical user journeys covered", "mutation_testing": "70% mutation score for critical components" }, "performance": { "test_execution": "Unit tests <5 minutes, Integration <15 minutes", "feedback_time": "Developer feedback within 10 minutes", "parallel_execution": "Tests run in parallel where possible", "flaky_tests": "<1% flaky test rate" }, "quality": { "test_reliability": "95% pass rate on consecutive runs", "test_maintainability": "Tests updated with code changes", "test_readability": "Tests serve as documentation", "test_isolation": "Tests don't depend on each other" } }, "codeTemplates": { "unitTest": { "framework": "Jest/Vitest", "template": "import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';\nimport { UserService } from './UserService';\nimport { MockUserRepository } from './__mocks__/UserRepository';\n\ndescribe('UserService', () => {\n let userService: UserService;\n let mockRepository: MockUserRepository;\n\n beforeEach(() => {\n mockRepository = new MockUserRepository();\n userService = new UserService(mockRepository);\n });\n\n afterEach(() => {\n vi.clearAllMocks();\n });\n\n describe('createUser', () => {\n it('should create user with valid data', async () => {\n // Arrange\n const userData = {\n name: 'John Doe',\n email: 'john@example.com',\n password: 'securePassword123'\n };\n const expectedUser = { id: '123', ...userData };\n mockRepository.create.mockResolvedValue(expectedUser);\n\n // Act\n const result = await userService.createUser(userData);\n\n // Assert\n expect(result).toEqual(expectedUser);\n expect(mockRepository.create).toHaveBeenCalledWith(userData);\n expect(mockRepository.create).toHaveBeenCalledTimes(1);\n });\n\n it('should throw error when email already exists', async () => {\n // Arrange\n const userData = {\n name: 'John Doe',\n email: 'existing@example.com',\n password: 'securePassword123'\n };\n mockRepository.findByEmail.mockResolvedValue({ id: '456', email: userData.email });\n\n // Act & Assert\n await expect(userService.createUser(userData))\n .rejects\n .toThrow('User with email already exists');\n \n expect(mockRepository.findByEmail).toHaveBeenCalledWith(userData.email);\n expect(mockRepository.create).not.toHaveBeenCalled();\n });\n\n it('should handle repository errors gracefully', async () => {\n // Arrange\n const userData = {\n name: 'John Doe',\n email: 'john@example.com',\n password: 'securePassword123'\n };\n const dbError = new Error('Database connection failed');\n mockRepository.create.mockRejectedValue(dbError);\n\n // Act & Assert\n await expect(userService.createUser(userData))\n .rejects\n .toThrow('Failed to create user');\n });\n });\n\n describe('getUserById', () => {\n it('should return user when found', async () => {\n // Arrange\n const userId = '123';\n const expectedUser = { id: userId, name: 'John Doe', email: 'john@example.com' };\n mockRepository.findById.mockResolvedValue(expectedUser);\n\n // Act\n const result = await userService.getUserById(userId);\n\n // Assert\n expect(result).toEqual(expectedUser);\n expect(mockRepository.findById).toHaveBeenCalledWith(userId);\n });\n\n it('should return null when user not found', async () => {\n // Arrange\n const userId = '999';\n mockRepository.findById.mockResolvedValue(null);\n\n // Act\n const result = await userService.getUserById(userId);\n\n // Assert\n expect(result).toBeNull();\n expect(mockRepository.findById).toHaveBeenCalledWith(userId);\n });\n });\n});" }, "integrationTest": { "framework": "Supertest + Jest", "template": "import request from 'supertest';\nimport { Test, TestingModule } from '@nestjs/testing';\nimport { INestApplication } from '@nestjs/common';\nimport { AppModule } from '../src/app.module';\nimport { DatabaseService } from '../src/database/database.service';\nimport { createTestUser, cleanupTestData } from './fixtures/user.fixtures';\n\ndescribe('Users API Integration Tests', () => {\n let app: INestApplication;\n let databaseService: DatabaseService;\n let testUserId: string;\n\n beforeAll(async () => {\n const moduleFixture: TestingModule = await Test.createTestingModule({\n imports: [AppModule],\n }).compile();\n\n app = moduleFixture.createNestApplication();\n databaseService = moduleFixture.get(DatabaseService);\n \n await app.init();\n \n // Set up test data\n const testUser = await createTestUser(databaseService);\n testUserId = testUser.id;\n });\n\n afterAll(async () => {\n // Clean up test data\n await cleanupTestData(databaseService);\n await app.close();\n });\n\n describe('POST /users', () => {\n it('should create a new user', async () => {\n const newUser = {\n name: 'Jane Doe',\n email: 'jane@example.com',\n password: 'securePassword123'\n };\n\n const response = await request(app.getHttpServer())\n .post('/users')\n .send(newUser)\n .expect(201);\n\n expect(response.body).toMatchObject({\n id: expect.any(String),\n name: newUser.name,\n email: newUser.email,\n createdAt: expect.any(String)\n });\n expect(response.body.password).toBeUndefined();\n\n // Verify user was actually created in database\n const createdUser = await databaseService.user.findUnique({\n where: { id: response.body.id }\n });\n expect(createdUser).toBeTruthy();\n expect(createdUser.name).toBe(newUser.name);\n });\n\n it('should return 400 for invalid data', async () => {\n const invalidUser = {\n name: '', // Invalid: empty name\n email: 'invalid-email', // Invalid: bad email format\n password: '123' // Invalid: too short\n };\n\n const response = await request(app.getHttpServer())\n .post('/users')\n .send(invalidUser)\n .expect(400);\n\n expect(response.body.errors).toBeDefined();\n expect(response.body.errors).toContain(\n expect.objectContaining({\n field: 'email',\n message: expect.stringContaining('valid email')\n })\n );\n });\n\n it('should return 409 for duplicate email', async () => {\n const duplicateUser = {\n name: 'John Duplicate',\n email: 'existing@example.com', // Email already exists\n password: 'securePassword123'\n };\n\n await request(app.getHttpServer())\n .post('/users')\n .send(duplicateUser)\n .expect(409);\n });\n });\n\n describe('GET /users/:id', () => {\n it('should return user when found', async () => {\n const response = await request(app.getHttpServer())\n .get(`/users/${testUserId}`)\n .expect(200);\n\n expect(response.body).toMatchObject({\n id: testUserId,\n name: expect.any(String),\n email: expect.any(String),\n createdAt: expect.any(String)\n });\n expect(response.body.password).toBeUndefined();\n });\n\n it('should return 404 for non-existent user', async () => {\n const nonExistentId = '999999';\n \n await request(app.getHttpServer())\n .get(`/users/${nonExistentId}`)\n .expect(404);\n });\n\n it('should return 400 for invalid user id format', async () => {\n await request(app.getHttpServer())\n .get('/users/invalid-id')\n .expect(400);\n });\n });\n\n describe('PUT /users/:id', () => {\n it('should update user successfully', async () => {\n const updateData = {\n name: 'Updated Name',\n email: 'updated@example.com'\n };\n\n const response = await request(app.getHttpServer())\n .put(`/users/${testUserId}`)\n .send(updateData)\n .expect(200);\n\n expect(response.body).toMatchObject({\n id: testUserId,\n name: updateData.name,\n email: updateData.email,\n updatedAt: expect.any(String)\n });\n\n // Verify update in database\n const updatedUser = await databaseService.user.findUnique({\n where: { id: testUserId }\n });\n expect(updatedUser.name).toBe(updateData.name);\n expect(updatedUser.email).toBe(updateData.email);\n });\n });\n});" }, "e2eTest": { "framework": "Playwright", "template": "import { test, expect } from '@playwright/test';\nimport { LoginPage } from '../pages/LoginPage';\nimport { DashboardPage } from '../pages/DashboardPage';\nimport { UserProfilePage } from '../pages/UserProfilePage';\n\ntest.describe('User Management E2E Tests', () => {\n let loginPage: LoginPage;\n let dashboardPage: DashboardPage;\n let userProfilePage: UserProfilePage;\n\n test.beforeEach(async ({ page }) => {\n loginPage = new LoginPage(page);\n dashboardPage = new DashboardPage(page);\n userProfilePage = new UserProfilePage(page);\n \n // Navigate to application\n await page.goto('/login');\n });\n\n test('complete user registration and profile update flow', async ({ page }) => {\n // Step 1: Register new user\n await loginPage.clickSignUpLink();\n \n const newUser = {\n name: 'Test User',\n email: `test${Date.now()}@example.com`,\n password: 'SecurePassword123!'\n };\n \n await loginPage.fillRegistrationForm(newUser);\n await loginPage.submitRegistration();\n \n // Verify registration success\n await expect(page.locator('[data-testid=\"registration-success\"]'))\n .toBeVisible();\n \n // Step 2: Login with new user\n await loginPage.login(newUser.email, newUser.password);\n \n // Verify successful login and dashboard access\n await expect(dashboardPage.welcomeMessage)\n .toContainText(`Welcome, ${newUser.name}`);\n \n // Step 3: Navigate to profile settings\n await dashboardPage.clickProfileMenu();\n await dashboardPage.clickProfileSettings();\n \n // Verify profile page loaded\n await expect(userProfilePage.profileForm).toBeVisible();\n \n // Step 4: Update profile information\n const updatedInfo = {\n name: 'Updated Test User',\n bio: 'This is my updated bio',\n phone: '+1234567890'\n };\n \n await userProfilePage.updateProfile(updatedInfo);\n await userProfilePage.saveChanges();\n \n // Verify update success\n await expect(userProfilePage.successMessage)\n .toContainText('Profile updated successfully');\n \n // Step 5: Verify changes persist after page reload\n await page.reload();\n \n await expect(userProfilePage.nameField)\n .toHaveValue(updatedInfo.name);\n await expect(userProfilePage.bioField)\n .toHaveValue(updatedInfo.bio);\n await expect(userProfilePage.phoneField)\n .toHaveValue(updatedInfo.phone);\n \n // Step 6: Test profile picture upload\n await userProfilePage.uploadProfilePicture('./fixtures/test-avatar.jpg');\n \n // Verify image upload\n await expect(userProfilePage.profileImage)\n .toBeVisible();\n \n // Step 7: Test account security settings\n await userProfilePage.clickSecurityTab();\n \n // Change password\n await userProfilePage.changePassword(\n newUser.password,\n 'NewSecurePassword123!'\n );\n \n await expect(userProfilePage.successMessage)\n .toContainText('Password changed successfully');\n \n // Step 8: Test logout and login with new password\n await dashboardPage.logout();\n \n await loginPage.login(newUser.email, 'NewSecurePassword123!');\n \n // Verify successful login with new password\n await expect(dashboardPage.welcomeMessage)\n .toContainText(`Welcome, ${updatedInfo.name}`);\n });\n\n test('should handle profile update errors gracefully', async ({ page }) => {\n // Login as existing user\n await loginPage.login('existing@example.com', 'password123');\n \n // Navigate to profile\n await dashboardPage.navigateToProfile();\n \n // Try to update with invalid data\n await userProfilePage.fillName(''); // Empty name should fail\n await userProfilePage.fillEmail('invalid-email'); // Invalid email\n await userProfilePage.saveChanges();\n \n // Verify error messages\n await expect(userProfilePage.nameError)\n .toContainText('Name is required');\n await expect(userProfilePage.emailError)\n .toContainText('Please enter a valid email');\n \n // Verify form wasn't submitted\n await expect(userProfilePage.successMessage)\n .not.toBeVisible();\n });\n\n test('should be accessible with keyboard navigation', async ({ page }) => {\n await loginPage.login('existing@example.com', 'password123');\n await dashboardPage.navigateToProfile();\n \n // Test keyboard navigation through form\n await page.keyboard.press('Tab'); // Focus name field\n await expect(userProfilePage.nameField).toBeFocused();\n \n await page.keyboard.press('Tab'); // Focus email field\n await expect(userProfilePage.emailField).toBeFocused();\n \n await page.keyboard.press('Tab'); // Focus bio field\n await expect(userProfilePage.bioField).toBeFocused();\n \n // Test form submission with keyboard\n await userProfilePage.fillName('Keyboard User');\n await page.keyboard.press('Enter'); // Should submit form\n \n await expect(userProfilePage.successMessage)\n .toContainText('Profile updated successfully');\n });\n});" } }, "testDataManagement": { "fixtures": { "description": "Pre-defined test data for consistent testing", "patterns": "Factory pattern, Builder pattern, Object mothers", "storage": "JSON files, Database seeds, In-memory objects" }, "factories": { "description": "Dynamic test data generation", "tools": "Faker.js, Factory Bot, Factory Girl", "benefits": "Unique data, Customizable, Realistic" }, "mocking": { "description": "Fake implementations for external dependencies", "types": "API mocks, Database mocks, Service mocks", "tools": "MSW, Nock, Sinon, Jest mocks" }, "cleanup": { "description": "Clean up test data after test execution", "strategies": "Database transactions, Cleanup hooks, Isolated test databases", "importance": "Prevent test interference, Maintain test isolation" } }, "bestPractices": [ "Write tests first (TDD) or alongside implementation", "Keep tests independent and isolated from each other", "Use descriptive test names that explain the scenario", "Follow the AAA pattern (Arrange, Act, Assert)", "Mock external dependencies to ensure test isolation", "Write both positive and negative test cases", "Keep tests simple and focused on one thing", "Use test data factories for consistent test data", "Clean up test data after test execution", "Run tests frequently during development", "Maintain tests as first-class code with proper refactoring", "Use code coverage as a guide, not a target" ], "antiPatterns": [ "Tests that depend on other tests or test order", "Tests that test implementation details instead of behavior", "Over-mocking that makes tests brittle", "Tests with unclear or generic names", "Tests that are too complex or test multiple things", "Ignoring or commenting out failing tests", "Tests that duplicate production logic", "Hard-coded test data that becomes outdated", "Tests that require manual setup or intervention", "Flaky tests that pass/fail randomly", "Tests that take too long to run", "Tests without proper assertions" ] }