cqt-agent
Version:
979 lines (815 loc) • 32.1 kB
Markdown
# 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{{}}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.