🔷 Testing Strategies in C# with TuskLang
Testing Strategies in C# with TuskLang
Overview
This guide covers comprehensive testing strategies for C# applications using TuskLang, including unit testing, integration testing, and test configuration patterns.
Table of Contents
1. Unit Testing 2. Integration Testing 3. Test Configuration 4. Mocking Strategies 5. Test Data Management 6. Performance Testing 7. TuskLang Integration
Unit Testing
Basic Unit Tests
[TestClass]
public class UserServiceTests
{
private UserService _userService;
private Mock<IUserRepository> _mockRepository;
private Mock<ILogger<UserService>> _mockLogger;
private TuskLang _testConfig; [TestInitialize]
public void Setup()
{
_mockRepository = new Mock<IUserRepository>();
_mockLogger = new Mock<ILogger<UserService>>();
_testConfig = new TuskLang("test-config.tsk");
_userService = new UserService(_mockRepository.Object, _mockLogger.Object, _testConfig);
}
[TestMethod]
public async Task GetUserById_ValidId_ReturnsUser()
{
// Arrange
var userId = 1;
var expectedUser = new User { Id = userId, Name = "Test User", Email = "test@example.com" };
_mockRepository.Setup(r => r.GetByIdAsync(userId))
.ReturnsAsync(expectedUser);
// Act
var result = await _userService.GetUserByIdAsync(userId);
// Assert
Assert.IsNotNull(result);
Assert.AreEqual(expectedUser.Id, result.Id);
Assert.AreEqual(expectedUser.Name, result.Name);
Assert.AreEqual(expectedUser.Email, result.Email);
_mockRepository.Verify(r => r.GetByIdAsync(userId), Times.Once);
}
[TestMethod]
public async Task GetUserById_InvalidId_ReturnsNull()
{
// Arrange
var userId = 999;
_mockRepository.Setup(r => r.GetByIdAsync(userId))
.ReturnsAsync((User)null);
// Act
var result = await _userService.GetUserByIdAsync(userId);
// Assert
Assert.IsNull(result);
_mockRepository.Verify(r => r.GetByIdAsync(userId), Times.Once);
}
[TestMethod]
public async Task CreateUser_ValidRequest_ReturnsCreatedUser()
{
// Arrange
var request = new CreateUserRequest
{
Name = "New User",
Email = "newuser@example.com"
};
var expectedUser = new User
{
Id = 1,
Name = request.Name,
Email = request.Email,
CreatedAt = DateTime.UtcNow
};
_mockRepository.Setup(r => r.CreateAsync(It.IsAny<User>()))
.ReturnsAsync(expectedUser);
// Act
var result = await _userService.CreateUserAsync(request);
// Assert
Assert.IsNotNull(result);
Assert.AreEqual(expectedUser.Id, result.Id);
Assert.AreEqual(request.Name, result.Name);
Assert.AreEqual(request.Email, result.Email);
_mockRepository.Verify(r => r.CreateAsync(It.IsAny<User>()), Times.Once);
}
[TestMethod]
public async Task CreateUser_DuplicateEmail_ThrowsException()
{
// Arrange
var request = new CreateUserRequest
{
Name = "Test User",
Email = "existing@example.com"
};
_mockRepository.Setup(r => r.GetByEmailAsync(request.Email))
.ReturnsAsync(new User { Email = request.Email });
// Act & Assert
await Assert.ThrowsExceptionAsync<InvalidOperationException>(
() => _userService.CreateUserAsync(request));
_mockRepository.Verify(r => r.GetByEmailAsync(request.Email), Times.Once);
_mockRepository.Verify(r => r.CreateAsync(It.IsAny<User>()), Times.Never);
}
}
TuskLang Configuration Testing
[TestClass]
public class TuskLangConfigurationTests
{
private TuskLang _tuskLang; [TestInitialize]
public void Setup()
{
// Create test configuration
var testConfig = @"
app.name = TestApp
app.environment = Test
database.connectionString = TestConnection
api.baseUrl = https://test-api.example.com
api.timeoutSeconds = 30
";
File.WriteAllText("test-config.tsk", testConfig);
_tuskLang = new TuskLang("test-config.tsk");
}
[TestMethod]
public void GetValue_ValidKey_ReturnsValue()
{
// Act
var appName = _tuskLang.GetValue<string>("app.name");
var environment = _tuskLang.GetValue<string>("app.environment");
var timeout = _tuskLang.GetValue<int>("api.timeoutSeconds");
// Assert
Assert.AreEqual("TestApp", appName);
Assert.AreEqual("Test", environment);
Assert.AreEqual(30, timeout);
}
[TestMethod]
public void GetValue_InvalidKey_ReturnsDefault()
{
// Act
var value = _tuskLang.GetValue<string>("invalid.key", "default");
// Assert
Assert.AreEqual("default", value);
}
[TestMethod]
public void SetValue_NewKey_SetsValue()
{
// Act
_tuskLang.SetValue("test.key", "test-value");
var value = _tuskLang.GetValue<string>("test.key");
// Assert
Assert.AreEqual("test-value", value);
}
[TestMethod]
public void HasValue_ExistingKey_ReturnsTrue()
{
// Act
var hasValue = _tuskLang.HasValue("app.name");
// Assert
Assert.IsTrue(hasValue);
}
[TestMethod]
public void HasValue_NonExistingKey_ReturnsFalse()
{
// Act
var hasValue = _tuskLang.HasValue("invalid.key");
// Assert
Assert.IsFalse(hasValue);
}
[TestCleanup]
public void Cleanup()
{
if (File.Exists("test-config.tsk"))
{
File.Delete("test-config.tsk");
}
}
}
Integration Testing
API Integration Tests
[TestClass]
public class UsersApiIntegrationTests
{
private WebApplicationFactory<Program> _factory;
private HttpClient _client;
private TuskLang _testConfig; [TestInitialize]
public void Setup()
{
_factory = new WebApplicationFactory<Program>()
.WithWebHostBuilder(builder =>
{
builder.ConfigureAppConfiguration((context, config) =>
{
config.AddInMemoryCollection(new Dictionary<string, string>
{
["app:environment"] = "Test",
["database:connectionString"] = "TestConnection",
["api:baseUrl"] = "https://test-api.example.com"
});
});
builder.ConfigureServices(services =>
{
// Replace real services with test doubles
services.AddScoped<IUserRepository, TestUserRepository>();
services.AddScoped<ITuskLang>(provider => _testConfig);
});
});
_client = _factory.CreateClient();
_testConfig = new TuskLang("test-config.tsk");
}
[TestMethod]
public async Task GetUsers_ReturnsUsersList()
{
// Arrange
var expectedUsers = new List<UserDto>
{
new() { Id = 1, Name = "User 1", Email = "user1@example.com" },
new() { Id = 2, Name = "User 2", Email = "user2@example.com" }
};
// Act
var response = await _client.GetAsync("/api/users");
var content = await response.Content.ReadAsStringAsync();
var users = JsonSerializer.Deserialize<List<UserDto>>(content);
// Assert
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
Assert.IsNotNull(users);
Assert.AreEqual(expectedUsers.Count, users.Count);
}
[TestMethod]
public async Task GetUser_ValidId_ReturnsUser()
{
// Arrange
var userId = 1;
// Act
var response = await _client.GetAsync($"/api/users/{userId}");
var content = await response.Content.ReadAsStringAsync();
var user = JsonSerializer.Deserialize<UserDto>(content);
// Assert
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
Assert.IsNotNull(user);
Assert.AreEqual(userId, user.Id);
}
[TestMethod]
public async Task GetUser_InvalidId_ReturnsNotFound()
{
// Arrange
var userId = 999;
// Act
var response = await _client.GetAsync($"/api/users/{userId}");
// Assert
Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode);
}
[TestMethod]
public async Task CreateUser_ValidRequest_ReturnsCreatedUser()
{
// Arrange
var request = new CreateUserRequest
{
Name = "New User",
Email = "newuser@example.com"
};
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
// Act
var response = await _client.PostAsync("/api/users", content);
var responseContent = await response.Content.ReadAsStringAsync();
var user = JsonSerializer.Deserialize<UserDto>(responseContent);
// Assert
Assert.AreEqual(HttpStatusCode.Created, response.StatusCode);
Assert.IsNotNull(user);
Assert.AreEqual(request.Name, user.Name);
Assert.AreEqual(request.Email, user.Email);
}
[TestCleanup]
public void Cleanup()
{
_client?.Dispose();
_factory?.Dispose();
}
}
Database Integration Tests
[TestClass]
public class DatabaseIntegrationTests
{
private DbContext _context;
private IUserRepository _repository;
private TuskLang _testConfig; [TestInitialize]
public void Setup()
{
// Create in-memory database for testing
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
_context = new AppDbContext(options);
_repository = new UserRepository(_context);
_testConfig = new TuskLang("test-config.tsk");
// Seed test data
SeedTestData();
}
private void SeedTestData()
{
var users = new List<User>
{
new() { Id = 1, Name = "Test User 1", Email = "user1@example.com", CreatedAt = DateTime.UtcNow },
new() { Id = 2, Name = "Test User 2", Email = "user2@example.com", CreatedAt = DateTime.UtcNow }
};
_context.Set<User>().AddRange(users);
_context.SaveChanges();
}
[TestMethod]
public async Task GetById_ExistingUser_ReturnsUser()
{
// Arrange
var userId = 1;
// Act
var user = await _repository.GetByIdAsync(userId);
// Assert
Assert.IsNotNull(user);
Assert.AreEqual(userId, user.Id);
Assert.AreEqual("Test User 1", user.Name);
}
[TestMethod]
public async Task GetById_NonExistingUser_ReturnsNull()
{
// Arrange
var userId = 999;
// Act
var user = await _repository.GetByIdAsync(userId);
// Assert
Assert.IsNull(user);
}
[TestMethod]
public async Task Create_ValidUser_ReturnsCreatedUser()
{
// Arrange
var newUser = new User
{
Name = "New User",
Email = "newuser@example.com",
CreatedAt = DateTime.UtcNow
};
// Act
var createdUser = await _repository.CreateAsync(newUser);
// Assert
Assert.IsNotNull(createdUser);
Assert.IsTrue(createdUser.Id > 0);
Assert.AreEqual(newUser.Name, createdUser.Name);
Assert.AreEqual(newUser.Email, createdUser.Email);
}
[TestMethod]
public async Task Update_ExistingUser_UpdatesUser()
{
// Arrange
var userId = 1;
var user = await _repository.GetByIdAsync(userId);
user.Name = "Updated User";
// Act
var updatedUser = await _repository.UpdateAsync(user);
// Assert
Assert.IsNotNull(updatedUser);
Assert.AreEqual("Updated User", updatedUser.Name);
}
[TestMethod]
public async Task Delete_ExistingUser_RemovesUser()
{
// Arrange
var userId = 1;
// Act
var success = await _repository.DeleteAsync(userId);
var deletedUser = await _repository.GetByIdAsync(userId);
// Assert
Assert.IsTrue(success);
Assert.IsNull(deletedUser);
}
[TestCleanup]
public void Cleanup()
{
_context?.Dispose();
}
}
Test Configuration
Test Configuration Management
public class TestConfigurationManager
{
private readonly Dictionary<string, string> _testSettings;
private readonly string _configFilePath; public TestConfigurationManager()
{
_testSettings = new Dictionary<string, string>();
_configFilePath = "test-config.tsk";
}
public TestConfigurationManager WithSetting(string key, string value)
{
_testSettings[key] = value;
return this;
}
public TestConfigurationManager WithDatabaseConnection(string connectionString)
{
_testSettings["database.connectionString"] = connectionString;
return this;
}
public TestConfigurationManager WithApiSettings(string baseUrl, string apiKey)
{
_testSettings["api.baseUrl"] = baseUrl;
_testSettings["api.apiKey"] = apiKey;
return this;
}
public TestConfigurationManager WithEnvironment(string environment)
{
_testSettings["app.environment"] = environment;
return this;
}
public TuskLang Build()
{
var configContent = string.Join("\n",
_testSettings.Select(kvp => $"{kvp.Key} = {kvp.Value}"));
File.WriteAllText(_configFilePath, configContent);
return new TuskLang(_configFilePath);
}
public void Cleanup()
{
if (File.Exists(_configFilePath))
{
File.Delete(_configFilePath);
}
}
}
[TestClass]
public class ConfigurationTests
{
[TestMethod]
public void TestConfiguration_WithCustomSettings_LoadsCorrectly()
{
// Arrange
var configManager = new TestConfigurationManager()
.WithEnvironment("Test")
.WithDatabaseConnection("TestConnection")
.WithApiSettings("https://test-api.example.com", "test-key");
// Act
var config = configManager.Build();
// Assert
Assert.AreEqual("Test", config.GetValue<string>("app.environment"));
Assert.AreEqual("TestConnection", config.GetValue<string>("database.connectionString"));
Assert.AreEqual("https://test-api.example.com", config.GetValue<string>("api.baseUrl"));
Assert.AreEqual("test-key", config.GetValue<string>("api.apiKey"));
// Cleanup
configManager.Cleanup();
}
}
Mocking Strategies
Advanced Mocking
[TestClass]
public class AdvancedMockingTests
{
private Mock<IUserRepository> _mockRepository;
private Mock<ILogger<UserService>> _mockLogger;
private Mock<ITuskLang> _mockConfig; [TestInitialize]
public void Setup()
{
_mockRepository = new Mock<IUserRepository>();
_mockLogger = new Mock<ILogger<UserService>>();
_mockConfig = new Mock<ITuskLang>();
}
[TestMethod]
public async Task GetUsers_WithPagination_ReturnsCorrectResults()
{
// Arrange
var query = new UserQueryParams { Page = 1, PageSize = 10 };
var expectedUsers = Enumerable.Range(1, 10)
.Select(i => new User { Id = i, Name = $"User {i}", Email = $"user{i}@example.com" })
.ToList();
var expectedResult = new PaginatedResult<User>
{
Items = expectedUsers,
TotalCount = 25,
Page = 1,
PageSize = 10
};
_mockRepository.Setup(r => r.GetUsersAsync(query))
.ReturnsAsync(expectedResult);
_mockConfig.Setup(c => c.GetValue<int>("api.maxResults", It.IsAny<int>()))
.Returns(100);
var userService = new UserService(_mockRepository.Object, _mockLogger.Object, _mockConfig.Object);
// Act
var result = await userService.GetUsersAsync(query);
// Assert
Assert.IsNotNull(result);
Assert.AreEqual(expectedUsers.Count, result.Items.Count());
Assert.AreEqual(25, result.TotalCount);
_mockRepository.Verify(r => r.GetUsersAsync(query), Times.Once);
_mockConfig.Verify(c => c.GetValue<int>("api.maxResults", It.IsAny<int>()), Times.Once);
}
[TestMethod]
public async Task CreateUser_WithValidation_ValidatesInput()
{
// Arrange
var request = new CreateUserRequest
{
Name = "",
Email = "invalid-email"
};
_mockConfig.Setup(c => c.GetValue<bool>("validation.enableStrict", It.IsAny<bool>()))
.Returns(true);
var userService = new UserService(_mockRepository.Object, _mockLogger.Object, _mockConfig.Object);
// Act & Assert
await Assert.ThrowsExceptionAsync<ValidationException>(
() => userService.CreateUserAsync(request));
_mockRepository.Verify(r => r.CreateAsync(It.IsAny<User>()), Times.Never);
}
[TestMethod]
public async Task UpdateUser_WithConcurrency_HandlesConflict()
{
// Arrange
var userId = 1;
var request = new UpdateUserRequest { Name = "Updated User" };
_mockRepository.Setup(r => r.UpdateAsync(It.IsAny<User>()))
.ThrowsAsync(new ConcurrencyException("User was modified by another user"));
_mockConfig.Setup(c => c.GetValue<int>("retry.maxAttempts", It.IsAny<int>()))
.Returns(3);
var userService = new UserService(_mockRepository.Object, _mockLogger.Object, _mockConfig.Object);
// Act & Assert
await Assert.ThrowsExceptionAsync<ConcurrencyException>(
() => userService.UpdateUserAsync(userId, request));
_mockRepository.Verify(r => r.UpdateAsync(It.IsAny<User>()), Times.Once);
}
}
Test Data Management
Test Data Builders
public class UserBuilder
{
private int _id = 1;
private string _name = "Test User";
private string _email = "test@example.com";
private DateTime _createdAt = DateTime.UtcNow;
private bool _isActive = true; public UserBuilder WithId(int id)
{
_id = id;
return this;
}
public UserBuilder WithName(string name)
{
_name = name;
return this;
}
public UserBuilder WithEmail(string email)
{
_email = email;
return this;
}
public UserBuilder WithCreatedAt(DateTime createdAt)
{
_createdAt = createdAt;
return this;
}
public UserBuilder WithIsActive(bool isActive)
{
_isActive = isActive;
return this;
}
public User Build()
{
return new User
{
Id = _id,
Name = _name,
Email = _email,
CreatedAt = _createdAt,
IsActive = _isActive
};
}
public static UserBuilder Create() => new UserBuilder();
}
public class CreateUserRequestBuilder
{
private string _name = "Test User";
private string _email = "test@example.com";
public CreateUserRequestBuilder WithName(string name)
{
_name = name;
return this;
}
public CreateUserRequestBuilder WithEmail(string email)
{
_email = email;
return this;
}
public CreateUserRequest Build()
{
return new CreateUserRequest
{
Name = _name,
Email = _email
};
}
public static CreateUserRequestBuilder Create() => new CreateUserRequestBuilder();
}
[TestClass]
public class TestDataBuilderTests
{
[TestMethod]
public void UserBuilder_CreatesValidUser()
{
// Act
var user = UserBuilder.Create()
.WithId(1)
.WithName("John Doe")
.WithEmail("john@example.com")
.WithIsActive(true)
.Build();
// Assert
Assert.AreEqual(1, user.Id);
Assert.AreEqual("John Doe", user.Name);
Assert.AreEqual("john@example.com", user.Email);
Assert.IsTrue(user.IsActive);
}
[TestMethod]
public void CreateUserRequestBuilder_CreatesValidRequest()
{
// Act
var request = CreateUserRequestBuilder.Create()
.WithName("Jane Doe")
.WithEmail("jane@example.com")
.Build();
// Assert
Assert.AreEqual("Jane Doe", request.Name);
Assert.AreEqual("jane@example.com", request.Email);
}
}
Performance Testing
Performance Test Framework
[TestClass]
public class PerformanceTests
{
private UserService _userService;
private Mock<IUserRepository> _mockRepository;
private TuskLang _testConfig; [TestInitialize]
public void Setup()
{
_mockRepository = new Mock<IUserRepository>();
_testConfig = new TuskLang("test-config.tsk");
_userService = new UserService(_mockRepository.Object, null, _testConfig);
}
[TestMethod]
public async Task GetUsers_Performance_MeetsRequirements()
{
// Arrange
var query = new UserQueryParams { Page = 1, PageSize = 100 };
var users = Enumerable.Range(1, 100)
.Select(i => new User { Id = i, Name = $"User {i}", Email = $"user{i}@example.com" })
.ToList();
_mockRepository.Setup(r => r.GetUsersAsync(query))
.ReturnsAsync(new PaginatedResult<User> { Items = users, TotalCount = 100 });
var stopwatch = Stopwatch.StartNew();
// Act
var result = await _userService.GetUsersAsync(query);
stopwatch.Stop();
// Assert
Assert.IsNotNull(result);
Assert.AreEqual(100, result.Items.Count());
Assert.Less(stopwatch.ElapsedMilliseconds, 100); // Should complete within 100ms
}
[TestMethod]
public async Task CreateUser_Performance_MeetsRequirements()
{
// Arrange
var request = CreateUserRequestBuilder.Create()
.WithName("Performance Test User")
.WithEmail("perf@example.com")
.Build();
var createdUser = new User { Id = 1, Name = request.Name, Email = request.Email };
_mockRepository.Setup(r => r.CreateAsync(It.IsAny<User>()))
.ReturnsAsync(createdUser);
var stopwatch = Stopwatch.StartNew();
// Act
var result = await _userService.CreateUserAsync(request);
stopwatch.Stop();
// Assert
Assert.IsNotNull(result);
Assert.Less(stopwatch.ElapsedMilliseconds, 50); // Should complete within 50ms
}
}
TuskLang Integration
TuskLang Test Utilities
public class TuskLangTestUtils
{
public static TuskLang CreateTestConfig(Dictionary<string, object> settings = null)
{
var configContent = new StringBuilder();
if (settings != null)
{
foreach (var (key, value) in settings)
{
configContent.AppendLine($"{key} = {value}");
}
} var configPath = $"test-config-{Guid.NewGuid()}.tsk";
File.WriteAllText(configPath, configContent.ToString());
return new TuskLang(configPath);
}
public static void CleanupTestConfig(TuskLang config)
{
if (config != null)
{
var configPath = config.GetType().GetField("_configPath",
BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(config) as string;
if (!string.IsNullOrEmpty(configPath) && File.Exists(configPath))
{
File.Delete(configPath);
}
}
}
public static TuskLang CreateMockConfig()
{
var settings = new Dictionary<string, object>
{
["app.name"] = "TestApp",
["app.environment"] = "Test",
["database.connectionString"] = "TestConnection",
["api.baseUrl"] = "https://test-api.example.com",
["api.timeoutSeconds"] = 30,
["api.maxResults"] = 100,
["validation.enableStrict"] = true,
["retry.maxAttempts"] = 3
};
return CreateTestConfig(settings);
}
}
[TestClass]
public class TuskLangIntegrationTests
{
private TuskLang _testConfig;
[TestInitialize]
public void Setup()
{
_testConfig = TuskLangTestUtils.CreateMockConfig();
}
[TestMethod]
public void TuskLang_Integration_WorksWithServices()
{
// Arrange
var mockRepository = new Mock<IUserRepository>();
var userService = new UserService(mockRepository.Object, null, _testConfig);
// Act & Assert
// The service should be able to use the TuskLang configuration
Assert.IsNotNull(userService);
}
[TestMethod]
public void TuskLang_Configuration_LoadsCorrectly()
{
// Act & Assert
Assert.AreEqual("TestApp", _testConfig.GetValue<string>("app.name"));
Assert.AreEqual("Test", _testConfig.GetValue<string>("app.environment"));
Assert.AreEqual(30, _testConfig.GetValue<int>("api.timeoutSeconds"));
Assert.AreEqual(100, _testConfig.GetValue<int>("api.maxResults"));
Assert.IsTrue(_testConfig.GetValue<bool>("validation.enableStrict"));
}
[TestCleanup]
public void Cleanup()
{
TuskLangTestUtils.CleanupTestConfig(_testConfig);
}
}
Summary
This comprehensive testing strategies guide covers:
- Unit Testing: Isolated testing of individual components with mocking - Integration Testing: End-to-end testing of API endpoints and database operations - Test Configuration: Flexible test configuration management - Mocking Strategies: Advanced mocking techniques for complex scenarios - Test Data Management: Builder patterns for creating test data - Performance Testing: Performance benchmarks and requirements validation - TuskLang Integration: Testing utilities and integration patterns
The patterns ensure comprehensive test coverage while maintaining test maintainability and reliability in C# applications using TuskLang.