// Comprehensive .NET Unit Test Template - Auto-Generated with xUnit // This template generates complete unit tests for .NET backend components using Xunit; using Moq; using FluentAssertions; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.AspNetCore.Http; using {{NAMESPACE}}.Controllers; using {{NAMESPACE}}.Services; using {{NAMESPACE}}.Models; using {{NAMESPACE}}.DTOs; using {{NAMESPACE}}.Repositories; using System; using System.Threading.Tasks; using System.Collections.Generic; using System.Security.Claims; using System.ComponentModel.DataAnnotations; namespace {{NAMESPACE}}.Tests.Controllers { /// /// Comprehensive unit tests for {{CONTROLLER_NAME}} /// Generated automatically by Hubtel Test Engineer /// public class {{CONTROLLER_NAME}}Tests : IDisposable { #region Test Setup and Data private readonly Mock<{{SERVICE_INTERFACE}}> _mockService; private readonly Mock> _mockLogger; private readonly {{CONTROLLER_NAME}} _controller; private readonly Mock _mockHttpContext; // Test data factory with comprehensive scenarios private static class TestDataFactory { public static readonly {{ENTITY_TYPE}} ValidEntity = new {{ENTITY_TYPE}} { {{#each VALID_ENTITY_PROPERTIES}} {{this.name}} = {{this.value}}, {{/each}} }; public static readonly {{ENTITY_TYPE}} InvalidEntity = new {{ENTITY_TYPE}} { {{#each INVALID_ENTITY_PROPERTIES}} {{this.name}} = {{this.value}}, // {{this.reason}} {{/each}} }; public static readonly {{DTO_TYPE}} ValidDto = new {{DTO_TYPE}} { {{#each VALID_DTO_PROPERTIES}} {{this.name}} = {{this.value}}, {{/each}} }; public static readonly List<{{ENTITY_TYPE}}> EntityCollection = new List<{{ENTITY_TYPE}}> { ValidEntity, new {{ENTITY_TYPE}} { {{#each ADDITIONAL_ENTITIES}}{{this.prop}} = {{this.value}}, {{/each}} }, }; // Edge case test data public static readonly object[] BoundaryValues = new object[] { {{#each BOUNDARY_VALUES}} {{this.value}}, // {{this.description}} {{/each}} }; // Malicious input test data public static readonly string[] MaliciousInputs = new string[] { "", "'; DROP TABLE {{ENTITY_TYPE}}s; --", "../../etc/passwd", "{{REPEATED_CHARACTER_1000}}", "\0\0\0null_bytes\0\0\0" }; } public {{CONTROLLER_NAME}}Tests() { _mockService = new Mock<{{SERVICE_INTERFACE}}>(); _mockLogger = new Mock>(); _mockHttpContext = new Mock(); _controller = new {{CONTROLLER_NAME}}(_mockService.Object, _mockLogger.Object) { ControllerContext = new ControllerContext { HttpContext = _mockHttpContext.Object } }; SetupDefaultMocks(); } private void SetupDefaultMocks() { var claims = new List { new Claim(ClaimTypes.NameIdentifier, "test-user-id"), new Claim(ClaimTypes.Name, "test-user"), new Claim(ClaimTypes.Role, "User") }; var identity = new ClaimsIdentity(claims, "TestAuth"); var principal = new ClaimsPrincipal(identity); _mockHttpContext.Setup(c => c.User).Returns(principal); } public void Dispose() { _controller?.Dispose(); _mockService?.Reset(); _mockLogger?.Reset(); _mockHttpContext?.Reset(); } #endregion #region Constructor Tests [Fact] public void Constructor_WithNullService_ThrowsArgumentNullException() { // Act & Assert var exception = Assert.Throws(() => new {{CONTROLLER_NAME}}(null, _mockLogger.Object)); exception.ParamName.Should().Be("{{SERVICE_PARAMETER_NAME}}"); } [Fact] public void Constructor_WithNullLogger_ThrowsArgumentNullException() { // Act & Assert var exception = Assert.Throws(() => new {{CONTROLLER_NAME}}(_mockService.Object, null)); exception.ParamName.Should().Be("logger"); } [Fact] public void Constructor_WithValidDependencies_InitializesCorrectly() { // Act var controller = new {{CONTROLLER_NAME}}(_mockService.Object, _mockLogger.Object); // Assert controller.Should().NotBeNull(); controller.Should().BeAssignableTo(); } #endregion {{#each CONTROLLER_ACTIONS}} #region {{this.name}} Action Tests [Fact] public async Task {{this.name}}_WithValidInput_ReturnsOkResult() { // Arrange var input = TestDataFactory.ValidDto; var expectedResult = TestDataFactory.ValidEntity; _mockService.Setup(s => s.{{this.serviceMethod}}(It.IsAny<{{this.inputType}}>())) .ReturnsAsync(expectedResult); // Act var result = await _controller.{{this.name}}(input); // Assert result.Should().BeOfType(); var okResult = (OkObjectResult)result; okResult.Value.Should().BeEquivalentTo(expectedResult); okResult.StatusCode.Should().Be(200); _mockService.Verify(s => s.{{this.serviceMethod}}( It.Is<{{this.inputType}}>(x => x.Equals(input))), Times.Once); } [Fact] public async Task {{this.name}}_WithNullInput_ReturnsBadRequest() { // Act var result = await _controller.{{this.name}}(null); // Assert result.Should().BeOfType(); var badRequestResult = (BadRequestObjectResult)result; badRequestResult.StatusCode.Should().Be(400); badRequestResult.Value.Should().NotBeNull(); } [Fact] public async Task {{this.name}}_WithInvalidModelState_ReturnsBadRequest() { // Arrange var invalidInput = TestDataFactory.InvalidEntity; _controller.ModelState.AddModelError("{{this.primaryProperty}}", "{{this.validationError}}"); // Act var result = await _controller.{{this.name}}(invalidInput); // Assert result.Should().BeOfType(); var badRequestResult = (BadRequestObjectResult)result; badRequestResult.StatusCode.Should().Be(400); // Verify service was not called _mockService.Verify(s => s.{{this.serviceMethod}}(It.IsAny<{{this.inputType}}>()), Times.Never); } [Theory] [MemberData(nameof(GetInvalidInputTestData))] public async Task {{this.name}}_WithVariousInvalidInputs_ReturnsBadRequest( {{this.inputType}} invalidInput, string expectedErrorMessage) { // Act var result = await _controller.{{this.name}}(invalidInput); // Assert result.Should().BeOfType(); var badRequestResult = (BadRequestObjectResult)result; badRequestResult.Value.ToString().Should().Contain(expectedErrorMessage); } [Fact] public async Task {{this.name}}_ServiceThrowsArgumentException_ReturnsBadRequest() { // Arrange var input = TestDataFactory.ValidDto; var exceptionMessage = "Invalid argument provided to service"; _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(); var badRequestResult = (BadRequestObjectResult)result; badRequestResult.Value.ToString().Should().Contain(exceptionMessage); // Verify error was logged VerifyErrorLogged(exceptionMessage, LogLevel.Warning); } [Fact] public async Task {{this.name}}_ServiceThrowsUnauthorizedAccessException_ReturnsUnauthorized() { // Arrange var input = TestDataFactory.ValidDto; var exceptionMessage = "User not authorized for this operation"; _mockService.Setup(s => s.{{this.serviceMethod}}(It.IsAny<{{this.inputType}}>())) .ThrowsAsync(new UnauthorizedAccessException(exceptionMessage)); // Act var result = await _controller.{{this.name}}(input); // Assert result.Should().BeOfType(); var unauthorizedResult = (UnauthorizedObjectResult)result; unauthorizedResult.StatusCode.Should().Be(401); } {{#if this.returnsEntity}} [Fact] public async Task {{this.name}}_EntityNotFound_ReturnsNotFound() { // Arrange var input = TestDataFactory.ValidDto; _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(); var notFoundResult = (NotFoundObjectResult)result; notFoundResult.StatusCode.Should().Be(404); } {{/if}} [Fact] public async Task {{this.name}}_ServiceThrowsTimeoutException_ReturnsRequestTimeout() { // Arrange var input = TestDataFactory.ValidDto; _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(); var objectResult = (ObjectResult)result; objectResult.StatusCode.Should().Be(408); // Request Timeout } [Fact] public async Task {{this.name}}_ServiceThrowsUnexpectedException_ReturnsInternalServerError() { // Arrange var input = TestDataFactory.ValidDto; var exception = new InvalidOperationException("Unexpected service 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(); var objectResult = (ObjectResult)result; objectResult.StatusCode.Should().Be(500); // Verify critical error was logged VerifyErrorLogged("Unexpected service error", LogLevel.Error); } {{#if this.hasConcurrencyHandling}} [Fact] public async Task {{this.name}}_ConcurrencyConflict_ReturnsConflict() { // Arrange var input = TestDataFactory.ValidDto; _mockService.Setup(s => s.{{this.serviceMethod}}(It.IsAny<{{this.inputType}}>())) .ThrowsAsync(new InvalidOperationException("Concurrency conflict detected")); // Act var result = await _controller.{{this.name}}(input); // Assert result.Should().BeOfType(); var conflictResult = (ConflictObjectResult)result; conflictResult.StatusCode.Should().Be(409); } {{/if}} {{#if this.hasRateLimiting}} [Fact] public async Task {{this.name}}_RateLimitExceeded_ReturnsTooManyRequests() { // Arrange var input = TestDataFactory.ValidDto; _mockService.Setup(s => s.{{this.serviceMethod}}(It.IsAny<{{this.inputType}}>())) .ThrowsAsync(new InvalidOperationException("Rate limit exceeded")); // Act var result = await _controller.{{this.name}}(input); // Assert result.Should().BeOfType(); var objectResult = (ObjectResult)result; objectResult.StatusCode.Should().Be(429); // Too Many Requests } {{/if}} [Fact] public async Task {{this.name}}_SuccessfulExecution_LogsInformation() { // Arrange var input = TestDataFactory.ValidDto; var expectedResult = TestDataFactory.ValidEntity; _mockService.Setup(s => s.{{this.serviceMethod}}(It.IsAny<{{this.inputType}}>())) .ReturnsAsync(expectedResult); // Act await _controller.{{this.name}}(input); // Assert VerifyInformationLogged("{{this.name}} executed successfully"); } public static IEnumerable GetInvalidInputTestData() { {{#each this.invalidInputTests}} yield return new object[] { {{this.inputValue}}, "{{this.expectedErrorMessage}}" }; {{/each}} } #endregion {{/each}} #region Security Tests [Theory] [MemberData(nameof(GetMaliciousInputs))] public async Task {{PRIMARY_ACTION}}_WithMaliciousInput_SanitizesAndRejects(string maliciousInput) { // Arrange var input = TestDataFactory.ValidDto; // Inject malicious input into string properties SetStringProperty(input, "{{PRIMARY_STRING_PROPERTY}}", maliciousInput); // Act var result = await _controller.{{PRIMARY_ACTION}}(input); // Assert result.Should().BeOfType(); // Verify malicious input was never passed to service _mockService.Verify(s => s.{{PRIMARY_SERVICE_METHOD}}( It.Is<{{PRIMARY_INPUT_TYPE}}>(x => ContainsMaliciousContent(x, maliciousInput))), Times.Never); // Verify security incident was logged VerifySecurityIncidentLogged(maliciousInput); } [Fact] public async Task {{PRIMARY_ACTION}}_WithSqlInjectionAttempt_PreventsInjection() { // Arrange var sqlInjection = "'; DROP TABLE {{ENTITY_TYPE}}s; SELECT * FROM Users WHERE 1=1 --"; var input = TestDataFactory.ValidDto; SetStringProperty(input, "{{PRIMARY_STRING_PROPERTY}}", sqlInjection); // Act var result = await _controller.{{PRIMARY_ACTION}}(input); // Assert result.Should().BeOfType(); VerifySecurityIncidentLogged("SQL injection attempt detected"); } [Fact] public async Task {{PRIMARY_ACTION}}_WithXssAttempt_SanitizesInput() { // Arrange var xssPayload = ""; var input = TestDataFactory.ValidDto; SetStringProperty(input, "{{PRIMARY_STRING_PROPERTY}}", xssPayload); // Act var result = await _controller.{{PRIMARY_ACTION}}(input); // Assert result.Should().BeOfType(); VerifySecurityIncidentLogged("XSS attempt detected"); } public static IEnumerable GetMaliciousInputs() { foreach (var maliciousInput in TestDataFactory.MaliciousInputs) { yield return new object[] { maliciousInput }; } } #endregion #region Performance Tests [Fact] public async Task {{PRIMARY_ACTION}}_WithLargeValidPayload_CompletesWithinTimeLimit() { // Arrange var largeInput = GenerateLargeTestInput(5000); var startTime = DateTime.UtcNow; _mockService.Setup(s => s.{{PRIMARY_SERVICE_METHOD}}(It.IsAny<{{PRIMARY_INPUT_TYPE}}>())) .ReturnsAsync(TestDataFactory.ValidEntity); // Act var result = await _controller.{{PRIMARY_ACTION}}(largeInput); // Assert var elapsed = DateTime.UtcNow - startTime; elapsed.Should().BeLessThan(TimeSpan.FromSeconds(10)); // 10 second performance limit result.Should().BeOfType(); } [Theory] [InlineData(1000)] [InlineData(5000)] [InlineData(10000)] public async Task {{PRIMARY_ACTION}}_WithVariousInputSizes_MaintainsPerformance(int inputSize) { // Arrange var input = GenerateLargeTestInput(inputSize); var startTime = DateTime.UtcNow; _mockService.Setup(s => s.{{PRIMARY_SERVICE_METHOD}}(It.IsAny<{{PRIMARY_INPUT_TYPE}}>())) .ReturnsAsync(TestDataFactory.ValidEntity); // Act await _controller.{{PRIMARY_ACTION}}(input); // Assert var elapsed = DateTime.UtcNow - startTime; var maxAllowedTime = TimeSpan.FromMilliseconds(inputSize * 0.1); // Scale with input size elapsed.Should().BeLessThan(maxAllowedTime); } private static {{PRIMARY_INPUT_TYPE}} GenerateLargeTestInput(int size) { return new {{PRIMARY_INPUT_TYPE}} { {{#each LARGE_INPUT_PROPERTIES}} {{this.name}} = {{this.generator}}(size), {{/each}} }; } #endregion #region Edge Cases and Boundary Tests [Theory] [MemberData(nameof(GetBoundaryValues))] public async Task {{PRIMARY_ACTION}}_WithBoundaryValues_HandlesCorrectly(object boundaryValue) { // Arrange var input = TestDataFactory.ValidDto; SetBoundaryValue(input, boundaryValue); _mockService.Setup(s => s.{{PRIMARY_SERVICE_METHOD}}(It.IsAny<{{PRIMARY_INPUT_TYPE}}>())) .ReturnsAsync(TestDataFactory.ValidEntity); // Act var result = await _controller.{{PRIMARY_ACTION}}(input); // Assert result.Should().NotBeNull(); // Boundary values should either succeed or fail gracefully result.Should().BeOneOf( typeof(OkObjectResult), typeof(BadRequestObjectResult)); } [Fact] public async Task {{PRIMARY_ACTION}}_WithMinimumValidInput_Succeeds() { // Arrange var minimalInput = CreateMinimalValidInput(); _mockService.Setup(s => s.{{PRIMARY_SERVICE_METHOD}}(It.IsAny<{{PRIMARY_INPUT_TYPE}}>())) .ReturnsAsync(TestDataFactory.ValidEntity); // Act var result = await _controller.{{PRIMARY_ACTION}}(minimalInput); // Assert result.Should().BeOfType(); } [Fact] public async Task {{PRIMARY_ACTION}}_WithMaximumValidInput_Succeeds() { // Arrange var maximalInput = CreateMaximalValidInput(); _mockService.Setup(s => s.{{PRIMARY_SERVICE_METHOD}}(It.IsAny<{{PRIMARY_INPUT_TYPE}}>())) .ReturnsAsync(TestDataFactory.ValidEntity); // Act var result = await _controller.{{PRIMARY_ACTION}}(maximalInput); // Assert result.Should().BeOfType(); } public static IEnumerable GetBoundaryValues() { foreach (var boundaryValue in TestDataFactory.BoundaryValues) { yield return new object[] { boundaryValue }; } } #endregion #region Integration Workflow Tests [Fact] public async Task {{WORKFLOW_NAME}}_CompleteWorkflow_ExecutesCorrectly() { // Arrange var initialInput = TestDataFactory.ValidDto; var expectedFinalResult = TestDataFactory.ValidEntity; {{#each WORKFLOW_MOCKS}} _mockService.Setup(s => s.{{this.method}}(It.IsAny<{{this.inputType}}>())) .ReturnsAsync({{this.returnValue}}); {{/each}} // Act - Execute workflow steps {{#each WORKFLOW_STEPS}} var step{{@index}}Result = await _controller.{{this.action}}({{this.input}}); step{{@index}}Result.Should().BeOfType<{{this.expectedResultType}}>(); {{/each}} // Assert - Verify final result var finalResult = (OkObjectResult)step{{WORKFLOW_STEPS.length}}Result; finalResult.Value.Should().BeEquivalentTo(expectedFinalResult); // Verify all service interactions occurred in order {{#each WORKFLOW_VERIFICATIONS}} _mockService.Verify(s => s.{{this.method}}( It.IsAny<{{this.inputType}}>()), Times.{{this.expectedCalls}}); {{/each}} } #endregion #region Validation Tests [Theory] [InlineData("")] [InlineData(" ")] [InlineData(null)] public async Task {{PRIMARY_ACTION}}_WithInvalidStringValues_ReturnsValidationError(string invalidValue) { // Arrange var input = TestDataFactory.ValidDto; SetStringProperty(input, "{{PRIMARY_STRING_PROPERTY}}", invalidValue); // Act var result = await _controller.{{PRIMARY_ACTION}}(input); // Assert result.Should().BeOfType(); var badRequestResult = (BadRequestObjectResult)result; badRequestResult.Value.Should().NotBeNull(); } {{#each VALIDATION_RULES}} [Fact] public async Task {{../PRIMARY_ACTION}}_{{this.name}}Validation_{{this.testCase}}() { // Arrange var input = TestDataFactory.ValidDto; {{this.setupCode}} // Act var result = await _controller.{{../PRIMARY_ACTION}}(input); // Assert {{#if this.shouldPass}} result.Should().BeOfType(); {{else}} result.Should().BeOfType(); var badRequestResult = (BadRequestObjectResult)result; badRequestResult.Value.ToString().Should().Contain("{{this.expectedError}}"); {{/if}} } {{/each}} #endregion #region Helper Methods private void VerifyErrorLogged(string expectedMessage, LogLevel logLevel) { _mockLogger.Verify( x => x.Log( logLevel, It.IsAny(), It.Is((o, t) => o.ToString().Contains(expectedMessage)), It.IsAny(), It.IsAny>()), Times.AtLeastOnce); } private void VerifyInformationLogged(string expectedMessage) { VerifyErrorLogged(expectedMessage, LogLevel.Information); } private void VerifySecurityIncidentLogged(string incident) { _mockLogger.Verify( x => x.Log( LogLevel.Warning, It.IsAny(), It.Is((o, t) => o.ToString().Contains("Security incident") && o.ToString().Contains(incident)), It.IsAny(), It.IsAny>()), Times.AtLeastOnce); } private static void SetStringProperty(object obj, string propertyName, string value) { var property = obj.GetType().GetProperty(propertyName); property?.SetValue(obj, value); } private static bool ContainsMaliciousContent(object obj, string maliciousContent) { var objString = obj.ToString(); return objString.Contains(maliciousContent); } private static void SetBoundaryValue({{PRIMARY_INPUT_TYPE}} input, object boundaryValue) { {{#each BOUNDARY_SETTERS}} if (boundaryValue is {{this.type}}) { input.{{this.property}} = ({{this.type}})boundaryValue; } {{/each}} } private static {{PRIMARY_INPUT_TYPE}} CreateMinimalValidInput() { return new {{PRIMARY_INPUT_TYPE}} { {{#each MINIMAL_REQUIRED_PROPERTIES}} {{this.name}} = {{this.minValue}}, {{/each}} }; } private static {{PRIMARY_INPUT_TYPE}} CreateMaximalValidInput() { return new {{PRIMARY_INPUT_TYPE}} { {{#each MAXIMAL_PROPERTIES}} {{this.name}} = {{this.maxValue}}, {{/each}} }; } #endregion } } // === Service Layer Tests === namespace {{NAMESPACE}}.Tests.Services { public class {{SERVICE_NAME}}Tests : IDisposable { private readonly Mock<{{REPOSITORY_INTERFACE}}> _mockRepository; private readonly Mock> _mockLogger; private readonly {{SERVICE_NAME}} _service; public {{SERVICE_NAME}}Tests() { _mockRepository = new Mock<{{REPOSITORY_INTERFACE}}>(); _mockLogger = new Mock>(); _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.testInput}}; var expectedResult = {{this.expectedOutput}}; _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}}_WithNullInput_ThrowsArgumentNullException() { // Act & Assert await Assert.ThrowsAsync(() => _service.{{this.name}}(null)); } [Theory] [MemberData(nameof(Get{{this.name}}InvalidInputs))] public async Task {{this.name}}_WithInvalidInput_ThrowsArgumentException( {{this.inputType}} invalidInput, string expectedMessage) { // Act & Assert var exception = await Assert.ThrowsAsync(() => _service.{{this.name}}(invalidInput)); exception.Message.Should().Contain(expectedMessage); } public static IEnumerable Get{{this.name}}InvalidInputs() { {{#each this.invalidInputs}} yield return new object[] { {{this.value}}, "{{this.errorMessage}}" }; {{/each}} } {{/each}} } } // === Repository Tests === namespace {{NAMESPACE}}.Tests.Repositories { public class {{REPOSITORY_NAME}}Tests : IDisposable { // Repository tests would include database integration tests // This template focuses on unit tests, so repository tests are simplified public void Dispose() { // Cleanup resources } } }