UNPKG

cqt-agent

Version:
979 lines (815 loc) 32.1 kB
# Auto Unit Test Generator This task automatically generates comprehensive unit tests for all components, services, and utilities in the project by analyzing the codebase structure without requiring manual component specification. ## Objective Generate complete unit test suites covering all testable code units including components, services, utilities, hooks, and business logic functions with comprehensive test scenarios. ## Prerequisites - Project must have identifiable code structure - Testing framework must be configured (Vitest, Jest, NUnit) - Access to source code files and their dependencies ## Process ### Step 1: Comprehensive Code Discovery Automatically scan and identify all testable units: #### Frontend Code Discovery ```bash # Find all testable frontend units find src -name "*.tsx" -o -name "*.ts" -o -name "*.jsx" -o -name "*.js" | grep -v ".test." | grep -v ".spec." ``` **Discovers**: - **React/Vue Components**: Functional and class components - **Custom Hooks**: useAuth, useApi, useForm, etc. - **Utility Functions**: formatters, validators, helpers - **Service Modules**: API clients, data processors - **State Management**: stores, reducers, actions - **Context Providers**: authentication, theme, etc. #### Backend Code Discovery ```bash # Find all testable backend units find . -name "*.cs" -o -name "*.js" -o -name "*.ts" | grep -v ".test." | grep -v ".spec." ``` **Discovers**: - **Controllers**: API endpoint handlers - **Services**: Business logic implementations - **Repositories**: Data access layer - **Middleware**: Authentication, validation, logging - **Models/Entities**: Data models with methods - **Utilities**: Helper functions, extensions ### Step 2: Code Analysis & Test Strategy For each discovered unit, analyze: #### Function/Method Analysis ```javascript function analyzeCodeUnit(filePath) { const codeAnalysis = { unitType: determineUnitType(filePath), // component, service, utility, etc. functions: extractFunctions(filePath), dependencies: extractDependencies(filePath), complexity: calculateComplexity(filePath), testableScenarios: [] }; // Analyze each function for test scenarios codeAnalysis.functions.forEach(func => { codeAnalysis.testableScenarios.push(...generateTestScenarios(func)); }); return codeAnalysis; } function generateTestScenarios(func) { const scenarios = []; // Happy path scenarios scenarios.push({ type: 'happy_path', description: `${func.name} works correctly with valid inputs`, inputs: generateValidInputs(func.parameters), expectedBehavior: 'success' }); // Edge case scenarios if (func.parameters.length > 0) { scenarios.push({ type: 'edge_cases', description: `${func.name} handles edge cases`, inputs: generateEdgeCaseInputs(func.parameters), expectedBehavior: 'graceful_handling' }); } // Error scenarios scenarios.push({ type: 'error_cases', description: `${func.name} handles invalid inputs`, inputs: generateInvalidInputs(func.parameters), expectedBehavior: 'error_handling' }); // Async scenarios (if applicable) if (func.isAsync) { scenarios.push({ type: 'async_success', description: `${func.name} resolves correctly`, expectedBehavior: 'promise_resolution' }); scenarios.push({ type: 'async_failure', description: `${func.name} handles rejection`, expectedBehavior: 'promise_rejection' }); } return scenarios; } ``` ### Step 3: Comprehensive Unit Test Generation #### Frontend Unit Test Generation (React/Vue) ```typescript // Auto-generated comprehensive component test import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest'; import { {{COMPONENT_NAME}} } from './{{COMPONENT_PATH}}'; describe('{{COMPONENT_NAME}}', () => { // Setup and teardown beforeEach(() => { vi.clearAllMocks(); }); afterEach(() => { vi.restoreAllMocks(); }); // Rendering tests describe('Rendering', () => { it('renders without crashing', () => { render(<{{COMPONENT_NAME}} />); expect(screen.getByTestId('{{COMPONENT_TESTID}}')).toBeInTheDocument(); }); it('renders with required props', () => { const requiredProps = {{REQUIRED_PROPS}}; render(<{{COMPONENT_NAME}} {...requiredProps} />); expect(screen.getByTestId('{{COMPONENT_TESTID}}')).toBeInTheDocument(); }); it('renders with all props', () => { const allProps = {{ALL_PROPS}}; render(<{{COMPONENT_NAME}} {...allProps} />); expect(screen.getByTestId('{{COMPONENT_TESTID}}')).toBeInTheDocument(); }); it('applies custom className when provided', () => { const customClass = 'custom-test-class'; render(<{{COMPONENT_NAME}} className={customClass} />); expect(screen.getByTestId('{{COMPONENT_TESTID}}')).toHaveClass(customClass); }); }); // Interaction tests describe('User Interactions', () => { {{#each INTERACTIVE_ELEMENTS}} it('handles {{this.event}} on {{this.element}}', async () => { const mockHandler = vi.fn(); render(<{{COMPONENT_NAME}} {{this.propName}}={mockHandler} />); const element = screen.getBy{{this.selector}}('{{this.identifier}}'); fireEvent.{{this.event}}(element); expect(mockHandler).toHaveBeenCalledTimes(1); {{#if this.expectedArgs}} expect(mockHandler).toHaveBeenCalledWith({{this.expectedArgs}}); {{/if}} }); {{/each}} it('handles keyboard interactions', async () => { render(<{{COMPONENT_NAME}} />); const element = screen.getByTestId('{{COMPONENT_TESTID}}'); // Test Tab navigation fireEvent.keyDown(element, { key: 'Tab' }); expect(element).toHaveFocus(); // Test Enter key fireEvent.keyDown(element, { key: 'Enter' }); // Add specific assertions based on component behavior }); }); // State management tests describe('State Management', () => { {{#each STATE_VARIABLES}} it('manages {{this.name}} state correctly', async () => { render(<{{COMPONENT_NAME}} />); // Initial state expect(screen.getByTestId('{{this.testId}}')).toHaveTextContent('{{this.initialValue}}'); // State change const trigger = screen.getByTestId('{{this.trigger}}'); fireEvent.click(trigger); await waitFor(() => { expect(screen.getByTestId('{{this.testId}}')).toHaveTextContent('{{this.expectedValue}}'); }); }); {{/each}} }); // Props validation tests describe('Props Validation', () => { {{#each PROPS}} it('handles {{this.name}} prop correctly', () => { const testValue = {{this.testValue}}; render(<{{COMPONENT_NAME}} {{this.name}}={testValue} />); {{#if this.rendersContent}} expect(screen.getByText(testValue)).toBeInTheDocument(); {{/if}} {{#if this.affectsAttribute}} expect(screen.getByTestId('{{COMPONENT_TESTID}}')).toHaveAttribute('{{this.attribute}}', testValue); {{/if}} }); it('handles missing {{this.name}} prop gracefully', () => { render(<{{COMPONENT_NAME}} />); // Should not crash and should have default behavior expect(screen.getByTestId('{{COMPONENT_TESTID}}')).toBeInTheDocument(); }); {{/each}} }); // Error boundary tests describe('Error Handling', () => { it('handles rendering errors gracefully', () => { const consoleSpy = vi.spyOn(console, 'error').mockImplementation(); // Trigger error condition render(<{{COMPONENT_NAME}} {{ERROR_TRIGGERING_PROPS}} />); // Should not crash the test expect(screen.getByTestId('{{COMPONENT_TESTID}}')).toBeInTheDocument(); consoleSpy.mockRestore(); }); }); // Accessibility tests describe('Accessibility', () => { it('has proper ARIA attributes', () => { render(<{{COMPONENT_NAME}} />); const element = screen.getByTestId('{{COMPONENT_TESTID}}'); // Check for required ARIA attributes {{#each ARIA_ATTRIBUTES}} expect(element).toHaveAttribute('{{this.name}}', '{{this.value}}'); {{/each}} }); it('supports keyboard navigation', () => { render(<{{COMPONENT_NAME}} />); const element = screen.getByTestId('{{COMPONENT_TESTID}}'); element.focus(); expect(element).toHaveFocus(); fireEvent.keyDown(element, { key: 'Tab' }); // Verify tab order and focus management }); }); // Performance tests describe('Performance', () => { it('does not cause unnecessary re-renders', () => { const renderSpy = vi.fn(); const TestWrapper = (props) => { renderSpy(); return <{{COMPONENT_NAME}} {...props} />; }; const { rerender } = render(<TestWrapper />); expect(renderSpy).toHaveBeenCalledTimes(1); // Re-render with same props rerender(<TestWrapper />); expect(renderSpy).toHaveBeenCalledTimes(1); // Should not re-render }); }); }); ``` #### Service/Utility Function Tests ```typescript // Auto-generated service test import { describe, it, expect, vi, beforeEach } from 'vitest'; import { {{SERVICE_NAME}} } from './{{SERVICE_PATH}}'; describe('{{SERVICE_NAME}}', () => { beforeEach(() => { vi.clearAllMocks(); }); {{#each FUNCTIONS}} describe('{{this.name}}', () => { // Happy path tests it('works correctly with valid inputs', {{#if this.isAsync}}async {{/if}}() => { const validInput = {{this.validTestData}}; const expectedOutput = {{this.expectedOutput}}; {{#if this.isAsync}} const result = await {{SERVICE_NAME}}.{{this.name}}(validInput); {{else}} const result = {{SERVICE_NAME}}.{{this.name}}(validInput); {{/if}} expect(result).toEqual(expectedOutput); }); // Edge case tests {{#each this.edgeCases}} it('handles {{this.description}}', {{#if ../isAsync}}async {{/if}}() => { const edgeCaseInput = {{this.input}}; {{#if ../isAsync}} const result = await {{../SERVICE_NAME}}.{{../name}}(edgeCaseInput); {{else}} const result = {{../SERVICE_NAME}}.{{../name}}(edgeCaseInput); {{/if}} expect(result).toEqual({{this.expectedOutput}}); }); {{/each}} // Error handling tests {{#each this.errorScenarios}} it('throws error for {{this.description}}', {{#if ../isAsync}}async {{/if}}() => { const invalidInput = {{this.input}}; {{#if ../isAsync}} await expect({{../SERVICE_NAME}}.{{../name}}(invalidInput)).rejects.toThrow('{{this.expectedError}}'); {{else}} expect(() => {{../SERVICE_NAME}}.{{../name}}(invalidInput)).toThrow('{{this.expectedError}}'); {{/if}} }); {{/each}} // Mock/Dependency tests {{#if this.hasDependencies}} it('calls dependencies correctly', {{#if this.isAsync}}async {{/if}}() => { {{#each this.dependencies}} const mock{{this.name}} = vi.fn().mockReturnValue({{this.mockReturnValue}}); vi.mocked({{this.importName}}).mockImplementation(mock{{this.name}}); {{/each}} const input = {{this.testInput}}; {{#if this.isAsync}} await {{../SERVICE_NAME}}.{{../name}}(input); {{else}} {{../SERVICE_NAME}}.{{../name}}(input); {{/if}} {{#each this.dependencies}} expect(mock{{this.name}}).toHaveBeenCalledWith({{this.expectedArgs}}); {{/each}} }); {{/if}} }); {{/each}} }); ``` #### Backend Unit Test Generation (.NET with xUnit) ```csharp // Auto-generated comprehensive .NET unit test using xUnit using Xunit; using Moq; using FluentAssertions; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using {{NAMESPACE}}.Controllers; using {{NAMESPACE}}.Services; using {{NAMESPACE}}.Models; using {{NAMESPACE}}.DTOs; using System; using System.Threading.Tasks; using System.Collections.Generic; namespace {{NAMESPACE}}.Tests.Controllers { public class {{CONTROLLER_NAME}}Tests : IDisposable { private readonly Mock<{{SERVICE_INTERFACE}}> _mockService; private readonly Mock<ILogger<{{CONTROLLER_NAME}}>> _mockLogger; private readonly {{CONTROLLER_NAME}} _controller; // Test data factory private static class TestData { public static readonly {{ENTITY_TYPE}} ValidEntity = new {{ENTITY_TYPE}} { {{#each ENTITY_PROPERTIES}} {{this.name}} = {{this.validValue}}, {{/each}} }; public static readonly {{ENTITY_TYPE}} InvalidEntity = new {{ENTITY_TYPE}} { {{#each ENTITY_PROPERTIES}} {{this.name}} = {{this.invalidValue}}, // {{this.invalidReason}} {{/each}} }; public static readonly List<{{ENTITY_TYPE}}> EntityList = new List<{{ENTITY_TYPE}}> { ValidEntity, new {{ENTITY_TYPE}} { /* additional test data */ } }; } public {{CONTROLLER_NAME}}Tests() { _mockService = new Mock<{{SERVICE_INTERFACE}}>(); _mockLogger = new Mock<ILogger<{{CONTROLLER_NAME}}>>(); _controller = new {{CONTROLLER_NAME}}(_mockService.Object, _mockLogger.Object); } public void Dispose() { _controller?.Dispose(); _mockService?.Reset(); _mockLogger?.Reset(); } #region Constructor Tests [Fact] public void Constructor_WithNullService_ThrowsArgumentNullException() { // Act & Assert Assert.Throws<ArgumentNullException>(() => new {{CONTROLLER_NAME}}(null, _mockLogger.Object)); } [Fact] public void Constructor_WithNullLogger_ThrowsArgumentNullException() { // Act & Assert Assert.Throws<ArgumentNullException>(() => new {{CONTROLLER_NAME}}(_mockService.Object, null)); } [Fact] public void Constructor_WithValidDependencies_SetsPropertiesCorrectly() { // Act var controller = new {{CONTROLLER_NAME}}(_mockService.Object, _mockLogger.Object); // Assert controller.Should().NotBeNull(); } #endregion {{#each CONTROLLER_ACTIONS}} #region {{this.name}} Tests [Fact] public async Task {{this.name}}_WithValidInput_ReturnsOkResult() { // Arrange var validInput = TestData.ValidEntity; var expectedResult = {{this.expectedResult}}; _mockService.Setup(s => s.{{this.serviceMethod}}(It.IsAny<{{this.inputType}}>())) .ReturnsAsync(expectedResult); // Act var result = await _controller.{{this.name}}(validInput); // Assert result.Should().BeOfType<OkObjectResult>(); var okResult = result as OkObjectResult; okResult.Value.Should().BeEquivalentTo(expectedResult); _mockService.Verify(s => s.{{this.serviceMethod}}(validInput), Times.Once); } [Fact] public async Task {{this.name}}_WithInvalidModelState_ReturnsBadRequest() { // Arrange var invalidInput = TestData.InvalidEntity; _controller.ModelState.AddModelError("{{this.errorProperty}}", "{{this.errorMessage}}"); // Act var result = await _controller.{{this.name}}(invalidInput); // Assert result.Should().BeOfType<BadRequestObjectResult>(); var badRequestResult = result as BadRequestObjectResult; badRequestResult.Value.Should().NotBeNull(); } [Fact] public async Task {{this.name}}_WithNullInput_ReturnsBadRequest() { // Act var result = await _controller.{{this.name}}(null); // Assert result.Should().BeOfType<BadRequestObjectResult>(); } [Theory] [MemberData(nameof(GetInvalidInputs))] public async Task {{this.name}}_WithInvalidInputs_ReturnsBadRequest({{this.inputType}} invalidInput, string expectedErrorMessage) { // Act var result = await _controller.{{this.name}}(invalidInput); // Assert result.Should().BeOfType<BadRequestObjectResult>(); var badRequestResult = result as BadRequestObjectResult; badRequestResult.Value.ToString().Should().Contain(expectedErrorMessage); } [Fact] public async Task {{this.name}}_ServiceThrowsArgumentException_ReturnsBadRequest() { // Arrange var input = TestData.ValidEntity; var exceptionMessage = "Invalid argument provided"; _mockService.Setup(s => s.{{this.serviceMethod}}(It.IsAny<{{this.inputType}}>())) .ThrowsAsync(new ArgumentException(exceptionMessage)); // Act var result = await _controller.{{this.name}}(input); // Assert result.Should().BeOfType<BadRequestObjectResult>(); var badRequestResult = result as BadRequestObjectResult; badRequestResult.Value.ToString().Should().Contain(exceptionMessage); } [Fact] public async Task {{this.name}}_ServiceThrowsUnexpectedException_ReturnsInternalServerError() { // Arrange var input = TestData.ValidEntity; var exception = new InvalidOperationException("Unexpected error"); _mockService.Setup(s => s.{{this.serviceMethod}}(It.IsAny<{{this.inputType}}>())) .ThrowsAsync(exception); // Act var result = await _controller.{{this.name}}(input); // Assert result.Should().BeOfType<ObjectResult>(); var objectResult = result as ObjectResult; objectResult.StatusCode.Should().Be(500); // Verify error logging _mockLogger.Verify( x => x.Log( LogLevel.Error, It.IsAny<EventId>(), It.Is<It.IsAnyType>((o, t) => o.ToString().Contains("Unexpected error")), It.IsAny<Exception>(), It.IsAny<Func<It.IsAnyType, Exception, string>>()), Times.Once); } {{#if this.hasAsyncTimeout}} [Fact] public async Task {{this.name}}_ServiceTimeout_ReturnsRequestTimeout() { // Arrange var input = TestData.ValidEntity; _mockService.Setup(s => s.{{this.serviceMethod}}(It.IsAny<{{this.inputType}}>())) .ThrowsAsync(new TimeoutException("Operation timed out")); // Act var result = await _controller.{{this.name}}(input); // Assert result.Should().BeOfType<ObjectResult>(); var objectResult = result as ObjectResult; objectResult.StatusCode.Should().Be(408); // Request Timeout } {{/if}} {{#if this.hasAuthorization}} [Fact] public async Task {{this.name}}_WithUnauthorizedUser_ReturnsUnauthorized() { // Arrange var input = TestData.ValidEntity; _mockService.Setup(s => s.{{this.serviceMethod}}(It.IsAny<{{this.inputType}}>())) .ThrowsAsync(new UnauthorizedAccessException("User not authorized")); // Act var result = await _controller.{{this.name}}(input); // Assert result.Should().BeOfType<UnauthorizedObjectResult>(); } {{/if}} {{#if this.hasNotFoundCase}} [Fact] public async Task {{this.name}}_EntityNotFound_ReturnsNotFound() { // Arrange var input = TestData.ValidEntity; _mockService.Setup(s => s.{{this.serviceMethod}}(It.IsAny<{{this.inputType}}>())) .ReturnsAsync(({{this.returnType}})null); // Act var result = await _controller.{{this.name}}(input); // Assert result.Should().BeOfType<NotFoundObjectResult>(); } {{/if}} {{#if this.hasConcurrencyHandling}} [Fact] public async Task {{this.name}}_ConcurrencyConflict_ReturnsConflict() { // Arrange var input = TestData.ValidEntity; _mockService.Setup(s => s.{{this.serviceMethod}}(It.IsAny<{{this.inputType}}>())) .ThrowsAsync(new InvalidOperationException("Concurrency conflict")); // Act var result = await _controller.{{this.name}}(input); // Assert result.Should().BeOfType<ConflictObjectResult>(); } {{/if}} public static IEnumerable<object[]> GetInvalidInputs() { {{#each this.invalidInputTests}} yield return new object[] { {{this.input}}, "{{this.expectedError}}" }; {{/each}} } #endregion {{/each}} {{#if HAS_SERVICE_LAYER_TESTS}} #region Service Layer Tests {{#each SERVICE_METHODS}} [Fact] public async Task {{this.name}}_WithValidInput_ReturnsExpectedResult() { // This would be in a separate ServiceTests file // Included here for completeness of the template } {{/each}} #endregion {{/if}} #region Integration-like Tests (within unit test scope) [Fact] public async Task {{MAIN_WORKFLOW}}_EndToEndWorkflow_WorksCorrectly() { // Arrange var input = TestData.ValidEntity; var expectedFinalResult = {{EXPECTED_WORKFLOW_RESULT}}; {{#each WORKFLOW_SETUP}} _mockService.Setup(s => s.{{this.method}}(It.IsAny<{{this.inputType}}>())) .ReturnsAsync({{this.returnValue}}); {{/each}} // Act {{#each WORKFLOW_STEPS}} var step{{@index}}Result = await _controller.{{this.action}}({{this.input}}); {{/each}} // Assert var finalResult = step{{WORKFLOW_STEPS.length}}Result as OkObjectResult; finalResult.Value.Should().BeEquivalentTo(expectedFinalResult); // Verify all service calls were made in correct order {{#each WORKFLOW_VERIFICATIONS}} _mockService.Verify(s => s.{{this.method}}(It.IsAny<{{this.inputType}}>()), Times.{{this.expectedTimes}}); {{/each}} } #endregion #region Performance Tests [Fact] public async Task {{PRIMARY_ACTION}}_WithLargeDataSet_CompletesWithinTimeLimit() { // Arrange var largeInput = GenerateLargeTestData(1000); var startTime = DateTime.UtcNow; _mockService.Setup(s => s.{{PRIMARY_SERVICE_METHOD}}(It.IsAny<{{PRIMARY_INPUT_TYPE}}>())) .ReturnsAsync({{LARGE_DATA_RESULT}}); // Act var result = await _controller.{{PRIMARY_ACTION}}(largeInput); // Assert var elapsed = DateTime.UtcNow - startTime; elapsed.Should().BeLessThan(TimeSpan.FromSeconds(5)); // 5 second limit result.Should().BeOfType<OkObjectResult>(); } private static {{PRIMARY_INPUT_TYPE}} GenerateLargeTestData(int count) { // Generate large test dataset return new {{PRIMARY_INPUT_TYPE}} { {{#each LARGE_DATA_PROPERTIES}} {{this.name}} = {{this.largeValueGenerator}}, {{/each}} }; } #endregion #region Edge Cases and Boundary Tests [Theory] [InlineData(int.MinValue)] [InlineData(-1)] [InlineData(0)] [InlineData(int.MaxValue)] public async Task {{PRIMARY_ACTION}}_WithBoundaryValues_HandlesCorrectly(int boundaryValue) { // Arrange var input = TestData.ValidEntity; // Modify input with boundary value {{BOUNDARY_VALUE_SETUP}} _mockService.Setup(s => s.{{PRIMARY_SERVICE_METHOD}}(It.IsAny<{{PRIMARY_INPUT_TYPE}}>())) .ReturnsAsync({{BOUNDARY_EXPECTED_RESULT}}); // Act var result = await _controller.{{PRIMARY_ACTION}}(input); // Assert result.Should().NotBeNull(); // Add specific boundary value assertions } [Fact] public async Task {{PRIMARY_ACTION}}_WithEmptyString_HandlesGracefully() { // Test string boundary cases } [Fact] public async Task {{PRIMARY_ACTION}}_WithNullString_HandlesGracefully() { // Test null string cases } [Fact] public async Task {{PRIMARY_ACTION}}_WithVeryLongString_HandlesCorrectly() { // Test string length limits } #endregion #region Security Tests [Theory] [InlineData("<script>alert('xss')</script>")] [InlineData("'; DROP TABLE Users; --")] [InlineData("../../etc/passwd")] public async Task {{PRIMARY_ACTION}}_WithMaliciousInput_SanitizesCorrectly(string maliciousInput) { // Arrange var input = TestData.ValidEntity; {{MALICIOUS_INPUT_SETUP}} // Act var result = await _controller.{{PRIMARY_ACTION}}(input); // Assert result.Should().BeOfType<BadRequestObjectResult>(); // Verify malicious input was rejected/sanitized _mockService.Verify(s => s.{{PRIMARY_SERVICE_METHOD}}( It.Is<{{PRIMARY_INPUT_TYPE}}>(x => !x.ToString().Contains(maliciousInput))), Times.Never); } #endregion #region Logging Tests [Fact] public async Task {{PRIMARY_ACTION}}_SuccessfulExecution_LogsInformation() { // Arrange var input = TestData.ValidEntity; _mockService.Setup(s => s.{{PRIMARY_SERVICE_METHOD}}(It.IsAny<{{PRIMARY_INPUT_TYPE}}>())) .ReturnsAsync({{SUCCESS_RESULT}}); // Act await _controller.{{PRIMARY_ACTION}}(input); // Assert _mockLogger.Verify( x => x.Log( LogLevel.Information, It.IsAny<EventId>(), It.Is<It.IsAnyType>((o, t) => o.ToString().Contains("{{PRIMARY_ACTION}}")), It.IsAny<Exception>(), It.IsAny<Func<It.IsAnyType, Exception, string>>()), Times.AtLeastOnce); } #endregion } } // === Separate Service Tests File === namespace {{NAMESPACE}}.Tests.Services { public class {{SERVICE_NAME}}Tests : IDisposable { private readonly Mock<{{REPOSITORY_INTERFACE}}> _mockRepository; private readonly Mock<ILogger<{{SERVICE_NAME}}>> _mockLogger; private readonly {{SERVICE_NAME}} _service; public {{SERVICE_NAME}}Tests() { _mockRepository = new Mock<{{REPOSITORY_INTERFACE}}>(); _mockLogger = new Mock<ILogger<{{SERVICE_NAME}}>>(); _service = new {{SERVICE_NAME}}(_mockRepository.Object, _mockLogger.Object); } public void Dispose() { _mockRepository?.Reset(); _mockLogger?.Reset(); } {{#each SERVICE_METHODS}} [Fact] public async Task {{this.name}}_WithValidInput_ReturnsExpectedResult() { // Arrange var input = {{this.validInput}}; var expectedResult = {{this.expectedResult}}; _mockRepository.Setup(r => r.{{this.repositoryMethod}}(It.IsAny<{{this.inputType}}>())) .ReturnsAsync(expectedResult); // Act var result = await _service.{{this.name}}(input); // Assert result.Should().BeEquivalentTo(expectedResult); _mockRepository.Verify(r => r.{{this.repositoryMethod}}(input), Times.Once); } [Fact] public async Task {{this.name}}_WithInvalidInput_ThrowsArgumentException() { // Arrange var invalidInput = {{this.invalidInput}}; // Act & Assert await Assert.ThrowsAsync<ArgumentException>(() => _service.{{this.name}}(invalidInput)); } {{/each}} } } ``` ### Step 4: Test Organization & Structure ``` tests/ ├── unit/ │ ├── components/ │ │ ├── auth/ │ │ │ ├── LoginForm.test.ts │ │ │ ├── RegisterForm.test.ts │ │ │ └── PasswordReset.test.ts │ │ ├── common/ │ │ │ ├── Button.test.ts │ │ │ ├── Modal.test.ts │ │ │ └── DataTable.test.ts │ │ └── dashboard/ │ │ ├── DashboardHeader.test.ts │ │ └── StatsWidget.test.ts │ ├── services/ │ │ ├── AuthService.test.ts │ │ ├── ApiClient.test.ts │ │ └── ValidationService.test.ts │ ├── hooks/ │ │ ├── useAuth.test.ts │ │ ├── useApi.test.ts │ │ └── useForm.test.ts │ ├── utils/ │ │ ├── formatters.test.ts │ │ ├── validators.test.ts │ │ └── helpers.test.ts │ └── stores/ │ ├── authStore.test.ts │ └── appStore.test.ts ``` ### Step 5: Automatic Test Execution & Validation After generating all unit tests: ```bash # Run all unit tests with coverage npm run test:unit -- --coverage --watchAll=false # Validate coverage meets requirements (>85%) npm run test:coverage-check # Generate coverage report npm run test:coverage-report ``` ### Step 6: Coverage Analysis & Gap Identification ```javascript function analyzeCoverage(coverageReport) { const gaps = { uncoveredFunctions: [], lowCoverageFiles: [], missingTestFiles: [] }; // Identify functions with no tests coverageReport.files.forEach(file => { if (file.functions.covered < file.functions.total) { gaps.uncoveredFunctions.push({ file: file.path, missing: file.functions.total - file.functions.covered }); } if (file.lines.pct < 85) { gaps.lowCoverageFiles.push({ file: file.path, coverage: file.lines.pct }); } }); return gaps; } ``` ## Success Criteria - **Comprehensive Coverage**: Unit tests for all discoverable components, services, and utilities - **High Coverage**: >85% line coverage, >80% branch coverage - **Quality Tests**: Meaningful assertions, proper mocking, error handling - **Automatic Execution**: All generated tests pass without manual intervention - **Performance**: Test suite executes in reasonable time (<5 minutes) - **Maintainability**: Clean, readable test code following best practices ## Command Examples ### Auto-Generate All Unit Tests ``` *auto-generate-unit-tests ``` ### Combined Auto-Generation ``` *auto-generate-unit-tests *auto-generate-e2e *validate-tests ``` This task provides complete automation of unit test generation, discovering and testing all code units without requiring manual component specification, ensuring comprehensive test coverage across the entire codebase.