๐Ÿ”ท Testing Frameworks in C# TuskLang

C# Documentation

Testing Frameworks in C# TuskLang

Overview

Comprehensive testing is essential for maintaining code quality and ensuring application reliability. This guide covers unit testing, integration testing, end-to-end testing, and testing best practices for C# TuskLang applications.

๐Ÿงช Unit Testing

Test Base Classes

public abstract class TestBase
{
    protected readonly ITestOutputHelper _output;
    protected readonly TSKConfig _config;
    protected readonly Mock<ILogger> _mockLogger;
    
    protected TestBase(ITestOutputHelper output)
    {
        _output = output;
        _config = CreateTestConfiguration();
        _mockLogger = new Mock<ILogger>();
    }
    
    protected virtual TSKConfig CreateTestConfiguration()
    {
        var config = new TSKConfig();
        
        // Set up test configuration
        config.Set("database.connection_string", "Data Source=:memory:");
        config.Set("app.environment", "test");
        config.Set("logging.level", "Debug");
        config.Set("cache.enabled", "false");
        
        return config;
    }
    
    protected virtual void WriteLine(string message)
    {
        _output.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] {message}");
    }
    
    protected virtual async Task<TSKConfig> LoadTestConfigurationAsync(string configPath)
    {
        var config = new TSKConfig();
        await config.LoadAsync(configPath);
        return config;
    }
}

public abstract class DatabaseTestBase : TestBase { protected readonly IDbConnection _connection; protected readonly IDbTransaction _transaction; protected DatabaseTestBase(ITestOutputHelper output) : base(output) { _connection = new SqliteConnection("Data Source=:memory:"); _connection.Open(); _transaction = _connection.BeginTransaction(); SetupDatabase(); } protected virtual void SetupDatabase() { // Create test tables var createUsersTable = @" CREATE TABLE users ( id TEXT PRIMARY KEY, email TEXT NOT NULL UNIQUE, first_name TEXT NOT NULL, last_name TEXT NOT NULL, status TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT )"; var createEventsTable = @" CREATE TABLE events ( id TEXT PRIMARY KEY, event_type TEXT NOT NULL, event_data TEXT NOT NULL, created_at TEXT NOT NULL, status TEXT NOT NULL )"; _connection.Execute(createUsersTable); _connection.Execute(createEventsTable); } protected virtual async Task<User> CreateTestUserAsync(string email = "test@example.com") { var user = new User { Id = Guid.NewGuid(), Email = email, FirstName = "Test", LastName = "User", Status = UserStatus.Active, CreatedAt = DateTime.UtcNow }; var query = @" INSERT INTO users (id, email, first_name, last_name, status, created_at) VALUES (@Id, @Email, @FirstName, @LastName, @Status, @CreatedAt)"; await _connection.ExecuteAsync(query, user); return user; } public override void Dispose() { _transaction?.Rollback(); _transaction?.Dispose(); _connection?.Dispose(); base.Dispose(); } }

User Service Tests

public class UserServiceTests : DatabaseTestBase
{
    private readonly UserService _userService;
    private readonly Mock<IEventBus> _mockEventBus;
    private readonly Mock<IHealthCheckService> _mockHealthCheckService;
    
    public UserServiceTests(ITestOutputHelper output) : base(output)
    {
        _mockEventBus = new Mock<IEventBus>();
        _mockHealthCheckService = new Mock<IHealthCheckService>();
        
        var userRepository = new UserRepository(_connection);
        
        _userService = new UserService(
            _mockLogger.Object,
            _config,
            Mock.Of<IServiceProvider>(),
            _mockHealthCheckService.Object,
            userRepository,
            _mockEventBus.Object);
    }
    
    [Fact]
    public async Task CreateUserAsync_ValidRequest_ShouldCreateUser()
    {
        // Arrange
        var request = new CreateUserRequest
        {
            Email = "test@example.com",
            FirstName = "John",
            LastName = "Doe"
        };
        
        _mockEventBus.Setup(x => x.PublishAsync(It.IsAny<UserCreatedEvent>()))
            .Returns(Task.CompletedTask);
        
        // Act
        var result = await _userService.CreateUserAsync(request);
        
        // Assert
        Assert.NotNull(result);
        Assert.NotEqual(Guid.Empty, result.Id);
        Assert.Equal(request.Email, result.Email);
        Assert.Equal(request.FirstName, result.FirstName);
        Assert.Equal(request.LastName, result.LastName);
        Assert.Equal(UserStatus.Active, result.Status);
        
        // Verify event was published
        _mockEventBus.Verify(x => x.PublishAsync(It.Is<UserCreatedEvent>(e => 
            e.Email == request.Email)), Times.Once);
        
        WriteLine($"Created user with ID: {result.Id}");
    }
    
    [Fact]
    public async Task CreateUserAsync_DuplicateEmail_ShouldThrowException()
    {
        // Arrange
        var existingUser = await CreateTestUserAsync("test@example.com");
        
        var request = new CreateUserRequest
        {
            Email = "test@example.com",
            FirstName = "Jane",
            LastName = "Doe"
        };
        
        // Act & Assert
        var exception = await Assert.ThrowsAsync<InvalidOperationException>(
            () => _userService.CreateUserAsync(request));
        
        Assert.Contains("already exists", exception.Message);
        WriteLine($"Expected exception caught: {exception.Message}");
    }
    
    [Fact]
    public async Task GetUserAsync_ExistingUser_ShouldReturnUser()
    {
        // Arrange
        var testUser = await CreateTestUserAsync();
        
        // Act
        var result = await _userService.GetUserAsync(testUser.Id);
        
        // Assert
        Assert.NotNull(result);
        Assert.Equal(testUser.Id, result.Id);
        Assert.Equal(testUser.Email, result.Email);
        Assert.Equal(testUser.FirstName, result.FirstName);
        Assert.Equal(testUser.LastName, result.LastName);
        
        WriteLine($"Retrieved user: {result.Email}");
    }
    
    [Fact]
    public async Task GetUserAsync_NonExistentUser_ShouldReturnNull()
    {
        // Arrange
        var nonExistentId = Guid.NewGuid();
        
        // Act
        var result = await _userService.GetUserAsync(nonExistentId);
        
        // Assert
        Assert.Null(result);
        WriteLine("Non-existent user correctly returned null");
    }
    
    [Fact]
    public async Task UpdateUserAsync_ExistingUser_ShouldUpdateUser()
    {
        // Arrange
        var testUser = await CreateTestUserAsync();
        
        var request = new UpdateUserRequest
        {
            FirstName = "Updated",
            LastName = "Name"
        };
        
        _mockEventBus.Setup(x => x.PublishAsync(It.IsAny<UserUpdatedEvent>()))
            .Returns(Task.CompletedTask);
        
        // Act
        await _userService.UpdateUserAsync(testUser.Id, request);
        
        // Assert
        var updatedUser = await _userService.GetUserAsync(testUser.Id);
        Assert.NotNull(updatedUser);
        Assert.Equal(request.FirstName, updatedUser.FirstName);
        Assert.Equal(request.LastName, updatedUser.LastName);
        
        // Verify event was published
        _mockEventBus.Verify(x => x.PublishAsync(It.Is<UserUpdatedEvent>(e => 
            e.UserId == testUser.Id)), Times.Once);
        
        WriteLine($"Updated user: {updatedUser.Email}");
    }
    
    [Fact]
    public async Task UpdateUserAsync_NonExistentUser_ShouldThrowException()
    {
        // Arrange
        var nonExistentId = Guid.NewGuid();
        var request = new UpdateUserRequest
        {
            FirstName = "Updated",
            LastName = "Name"
        };
        
        // Act & Assert
        var exception = await Assert.ThrowsAsync<NotFoundException>(
            () => _userService.UpdateUserAsync(nonExistentId, request));
        
        Assert.Contains("not found", exception.Message);
        WriteLine($"Expected exception caught: {exception.Message}");
    }
}

Configuration Tests

public class TSKConfigTests : TestBase
{
    public TSKConfigTests(ITestOutputHelper output) : base(output) { }
    
    [Fact]
    public void Get_ExistingKey_ShouldReturnValue()
    {
        // Arrange
        _config.Set("test.key", "test_value");
        
        // Act
        var result = _config.Get<string>("test.key");
        
        // Assert
        Assert.Equal("test_value", result);
        WriteLine($"Retrieved config value: {result}");
    }
    
    [Fact]
    public void Get_NonExistentKey_ShouldReturnDefault()
    {
        // Act
        var result = _config.Get<string>("non.existent.key", "default_value");
        
        // Assert
        Assert.Equal("default_value", result);
        WriteLine($"Retrieved default value: {result}");
    }
    
    [Fact]
    public void Get_WithTypeConversion_ShouldConvertValue()
    {
        // Arrange
        _config.Set("test.number", "42");
        _config.Set("test.boolean", "true");
        _config.Set("test.double", "3.14");
        
        // Act
        var number = _config.Get<int>("test.number");
        var boolean = _config.Get<bool>("test.boolean");
        var doubleValue = _config.Get<double>("test.double");
        
        // Assert
        Assert.Equal(42, number);
        Assert.True(boolean);
        Assert.Equal(3.14, doubleValue);
        
        WriteLine($"Converted values: {number}, {boolean}, {doubleValue}");
    }
    
    [Fact]
    public void Has_ExistingKey_ShouldReturnTrue()
    {
        // Arrange
        _config.Set("test.key", "value");
        
        // Act
        var result = _config.Has("test.key");
        
        // Assert
        Assert.True(result);
        WriteLine("Key exists: true");
    }
    
    [Fact]
    public void Has_NonExistentKey_ShouldReturnFalse()
    {
        // Act
        var result = _config.Has("non.existent.key");
        
        // Assert
        Assert.False(result);
        WriteLine("Key exists: false");
    }
    
    [Fact]
    public async Task LoadAsync_ValidFile_ShouldLoadConfiguration()
    {
        // Arrange
        var configPath = "test-config.tsk";
        var configContent = @"
            app.name = ""Test App""
            app.version = ""1.0.0""
            database.connection_string = ""test_connection""
        ";
        
        await File.WriteAllTextAsync(configPath, configContent);
        
        try
        {
            // Act
            await _config.LoadAsync(configPath);
            
            // Assert
            Assert.Equal("Test App", _config.Get<string>("app.name"));
            Assert.Equal("1.0.0", _config.Get<string>("app.version"));
            Assert.Equal("test_connection", _config.Get<string>("database.connection_string"));
            
            WriteLine("Configuration loaded successfully");
        }
        finally
        {
            File.Delete(configPath);
        }
    }
    
    [Fact]
    public void Set_NewKey_ShouldAddKey()
    {
        // Act
        _config.Set("new.key", "new_value");
        
        // Assert
        Assert.True(_config.Has("new.key"));
        Assert.Equal("new_value", _config.Get<string>("new.key"));
        
        WriteLine("New key added successfully");
    }
    
    [Fact]
    public void Set_ExistingKey_ShouldUpdateValue()
    {
        // Arrange
        _config.Set("existing.key", "old_value");
        
        // Act
        _config.Set("existing.key", "new_value");
        
        // Assert
        Assert.Equal("new_value", _config.Get<string>("existing.key"));
        
        WriteLine("Existing key updated successfully");
    }
}

๐Ÿ”— Integration Testing

Integration Test Base

public abstract class IntegrationTestBase : TestBase
{
    protected readonly TestServer _server;
    protected readonly HttpClient _client;
    protected readonly IServiceProvider _serviceProvider;
    
    protected IntegrationTestBase(ITestOutputHelper output) : base(output)
    {
        var builder = new WebHostBuilder()
            .UseStartup<TestStartup>()
            .ConfigureServices(services =>
            {
                services.AddSingleton(_config);
                ConfigureTestServices(services);
            });
        
        _server = new TestServer(builder);
        _client = _server.CreateClient();
        _serviceProvider = _server.Host.Services;
        
        SetupTestData();
    }
    
    protected virtual void ConfigureTestServices(IServiceCollection services)
    {
        // Override services for testing
        services.AddScoped<IDbConnection>(provider => 
            new SqliteConnection("Data Source=:memory:"));
        
        services.AddScoped<IEventBus, MockEventBus>();
        services.AddScoped<IEmailService, MockEmailService>();
    }
    
    protected virtual void SetupTestData()
    {
        // Setup test data in database
        using var scope = _serviceProvider.CreateScope();
        var connection = scope.ServiceProvider.GetRequiredService<IDbConnection>();
        
        connection.Open();
        
        var createUsersTable = @"
            CREATE TABLE users (
                id TEXT PRIMARY KEY,
                email TEXT NOT NULL UNIQUE,
                first_name TEXT NOT NULL,
                last_name TEXT NOT NULL,
                status TEXT NOT NULL,
                created_at TEXT NOT NULL,
                updated_at TEXT
            )";
        
        connection.Execute(createUsersTable);
    }
    
    protected virtual async Task<User> CreateTestUserAsync(string email = "test@example.com")
    {
        using var scope = _serviceProvider.CreateScope();
        var userRepository = scope.ServiceProvider.GetRequiredService<IUserRepository>();
        
        var user = new User
        {
            Id = Guid.NewGuid(),
            Email = email,
            FirstName = "Test",
            LastName = "User",
            Status = UserStatus.Active,
            CreatedAt = DateTime.UtcNow
        };
        
        await userRepository.CreateAsync(user);
        return user;
    }
    
    public override void Dispose()
    {
        _client?.Dispose();
        _server?.Dispose();
        base.Dispose();
    }
}

public class TestStartup { public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddScoped<IUserRepository, UserRepository>(); services.AddScoped<UserService>(); services.AddScoped<IEventBus, MockEventBus>(); services.AddScoped<IEmailService, MockEmailService>(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } }

public class MockEventBus : IEventBus { public List<object> PublishedEvents { get; } = new(); public Task PublishAsync<T>(T @event) where T : class { PublishedEvents.Add(@event); return Task.CompletedTask; } public Task SubscribeAsync<T>(IEventHandler<T> handler) where T : class { return Task.CompletedTask; } public Task UnsubscribeAsync<T>(IEventHandler<T> handler) where T : class { return Task.CompletedTask; } }

public class MockEmailService : IEmailService { public List<EmailRequest> SentEmails { get; } = new(); public Task SendEmailAsync(EmailRequest request) { SentEmails.Add(request); return Task.CompletedTask; } public Task SendWelcomeEmailAsync(string email) { SentEmails.Add(new EmailRequest { To = email, Subject = "Welcome", Body = "Welcome to our service!" }); return Task.CompletedTask; } }

API Integration Tests

public class UserControllerIntegrationTests : IntegrationTestBase
{
    public UserControllerIntegrationTests(ITestOutputHelper output) : base(output) { }
    
    [Fact]
    public async Task CreateUser_ValidRequest_ShouldReturnCreatedUser()
    {
        // Arrange
        var request = new CreateUserRequest
        {
            Email = "test@example.com",
            FirstName = "John",
            LastName = "Doe"
        };
        
        var json = JsonSerializer.Serialize(request);
        var content = new StringContent(json, Encoding.UTF8, "application/json");
        
        // Act
        var response = await _client.PostAsync("/api/users", content);
        
        // Assert
        Assert.Equal(HttpStatusCode.Created, response.StatusCode);
        
        var responseContent = await response.Content.ReadAsStringAsync();
        var createdUser = JsonSerializer.Deserialize<UserDto>(responseContent, new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        });
        
        Assert.NotNull(createdUser);
        Assert.Equal(request.Email, createdUser.Email);
        Assert.Equal(request.FirstName, createdUser.FirstName);
        Assert.Equal(request.LastName, createdUser.LastName);
        
        WriteLine($"Created user via API: {createdUser.Id}");
    }
    
    [Fact]
    public async Task CreateUser_DuplicateEmail_ShouldReturnBadRequest()
    {
        // Arrange
        var existingUser = await CreateTestUserAsync("test@example.com");
        
        var request = new CreateUserRequest
        {
            Email = "test@example.com",
            FirstName = "Jane",
            LastName = "Doe"
        };
        
        var json = JsonSerializer.Serialize(request);
        var content = new StringContent(json, Encoding.UTF8, "application/json");
        
        // Act
        var response = await _client.PostAsync("/api/users", content);
        
        // Assert
        Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
        
        var responseContent = await response.Content.ReadAsStringAsync();
        Assert.Contains("already exists", responseContent);
        
        WriteLine("Duplicate email correctly rejected");
    }
    
    [Fact]
    public async Task GetUser_ExistingUser_ShouldReturnUser()
    {
        // Arrange
        var testUser = await CreateTestUserAsync();
        
        // Act
        var response = await _client.GetAsync($"/api/users/{testUser.Id}");
        
        // Assert
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
        
        var responseContent = await response.Content.ReadAsStringAsync();
        var user = JsonSerializer.Deserialize<UserDto>(responseContent, new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        });
        
        Assert.NotNull(user);
        Assert.Equal(testUser.Id, user.Id);
        Assert.Equal(testUser.Email, user.Email);
        
        WriteLine($"Retrieved user via API: {user.Email}");
    }
    
    [Fact]
    public async Task GetUser_NonExistentUser_ShouldReturnNotFound()
    {
        // Arrange
        var nonExistentId = Guid.NewGuid();
        
        // Act
        var response = await _client.GetAsync($"/api/users/{nonExistentId}");
        
        // Assert
        Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
        
        WriteLine("Non-existent user correctly returned 404");
    }
    
    [Fact]
    public async Task UpdateUser_ExistingUser_ShouldUpdateUser()
    {
        // Arrange
        var testUser = await CreateTestUserAsync();
        
        var request = new UpdateUserRequest
        {
            FirstName = "Updated",
            LastName = "Name"
        };
        
        var json = JsonSerializer.Serialize(request);
        var content = new StringContent(json, Encoding.UTF8, "application/json");
        
        // Act
        var response = await _client.PutAsync($"/api/users/{testUser.Id}", content);
        
        // Assert
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
        
        // Verify user was updated
        var getUserResponse = await _client.GetAsync($"/api/users/{testUser.Id}");
        var userContent = await getUserResponse.Content.ReadAsStringAsync();
        var updatedUser = JsonSerializer.Deserialize<UserDto>(userContent, new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        });
        
        Assert.NotNull(updatedUser);
        Assert.Equal(request.FirstName, updatedUser.FirstName);
        Assert.Equal(request.LastName, updatedUser.LastName);
        
        WriteLine($"Updated user via API: {updatedUser.Email}");
    }
    
    [Fact]
    public async Task CreateUser_ShouldPublishEvent()
    {
        // Arrange
        var request = new CreateUserRequest
        {
            Email = "test@example.com",
            FirstName = "John",
            LastName = "Doe"
        };
        
        var json = JsonSerializer.Serialize(request);
        var content = new StringContent(json, Encoding.UTF8, "application/json");
        
        // Act
        await _client.PostAsync("/api/users", content);
        
        // Assert
        using var scope = _serviceProvider.CreateScope();
        var eventBus = scope.ServiceProvider.GetRequiredService<MockEventBus>();
        
        Assert.Single(eventBus.PublishedEvents);
        Assert.IsType<UserCreatedEvent>(eventBus.PublishedEvents[0]);
        
        var userCreatedEvent = (UserCreatedEvent)eventBus.PublishedEvents[0];
        Assert.Equal(request.Email, userCreatedEvent.Email);
        
        WriteLine("User created event published successfully");
    }
}

๐ŸŒ End-to-End Testing

E2E Test Base

public abstract class E2ETestBase : TestBase
{
    protected readonly IWebDriver _driver;
    protected readonly WebDriverWait _wait;
    protected readonly string _baseUrl;
    
    protected E2ETestBase(ITestOutputHelper output) : base(output)
    {
        var options = new ChromeOptions();
        options.AddArgument("--headless");
        options.AddArgument("--no-sandbox");
        options.AddArgument("--disable-dev-shm-usage");
        
        _driver = new ChromeDriver(options);
        _wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(10));
        
        _baseUrl = _config.Get<string>("app.base_url", "http://localhost:5000");
    }
    
    protected virtual async Task NavigateToAsync(string path)
    {
        var url = $"{_baseUrl}{path}";
        _driver.Navigate().GoToUrl(url);
        await Task.Delay(1000); // Wait for page load
        
        WriteLine($"Navigated to: {url}");
    }
    
    protected virtual async Task<IWebElement> WaitForElementAsync(By by)
    {
        var element = _wait.Until(driver => driver.FindElement(by));
        await Task.Delay(500); // Small delay for stability
        return element;
    }
    
    protected virtual async Task ClickAsync(By by)
    {
        var element = await WaitForElementAsync(by);
        element.Click();
        await Task.Delay(500);
        
        WriteLine($"Clicked element: {by}");
    }
    
    protected virtual async Task TypeAsync(By by, string text)
    {
        var element = await WaitForElementAsync(by);
        element.Clear();
        element.SendKeys(text);
        await Task.Delay(500);
        
        WriteLine($"Typed '{text}' into element: {by}");
    }
    
    protected virtual async Task<string> GetTextAsync(By by)
    {
        var element = await WaitForElementAsync(by);
        var text = element.Text;
        
        WriteLine($"Got text '{text}' from element: {by}");
        return text;
    }
    
    protected virtual async Task<bool> ElementExistsAsync(By by)
    {
        try
        {
            await WaitForElementAsync(by);
            return true;
        }
        catch (WebDriverTimeoutException)
        {
            return false;
        }
    }
    
    public override void Dispose()
    {
        _driver?.Quit();
        _driver?.Dispose();
        base.Dispose();
    }
}

public class UserManagementE2ETests : E2ETestBase { public UserManagementE2ETests(ITestOutputHelper output) : base(output) { } [Fact] public async Task CreateUser_ShouldDisplaySuccessMessage() { // Arrange await NavigateToAsync("/users/create"); var email = $"test_{Guid.NewGuid()}@example.com"; var firstName = "John"; var lastName = "Doe"; // Act await TypeAsync(By.Id("email"), email); await TypeAsync(By.Id("firstName"), firstName); await TypeAsync(By.Id("lastName"), lastName); await ClickAsync(By.Id("createButton")); // Assert var successMessage = await WaitForElementAsync(By.ClassName("success-message")); Assert.Contains("User created successfully", successMessage.Text); WriteLine("User creation E2E test passed"); } [Fact] public async Task CreateUser_DuplicateEmail_ShouldDisplayErrorMessage() { // Arrange await NavigateToAsync("/users/create"); var email = "duplicate@example.com"; var firstName = "John"; var lastName = "Doe"; // Create user first time await TypeAsync(By.Id("email"), email); await TypeAsync(By.Id("firstName"), firstName); await TypeAsync(By.Id("lastName"), lastName); await ClickAsync(By.Id("createButton")); // Wait for success await WaitForElementAsync(By.ClassName("success-message")); // Navigate back to create form await NavigateToAsync("/users/create"); // Act - Try to create duplicate await TypeAsync(By.Id("email"), email); await TypeAsync(By.Id("firstName"), "Jane"); await TypeAsync(By.Id("lastName"), "Doe"); await ClickAsync(By.Id("createButton")); // Assert var errorMessage = await WaitForElementAsync(By.ClassName("error-message")); Assert.Contains("already exists", errorMessage.Text); WriteLine("Duplicate email E2E test passed"); } [Fact] public async Task ViewUser_ShouldDisplayUserDetails() { // Arrange var userId = await CreateTestUserViaApiAsync(); await NavigateToAsync($"/users/{userId}"); // Act & Assert var emailElement = await WaitForElementAsync(By.Id("userEmail")); var firstNameElement = await WaitForElementAsync(By.Id("userFirstName")); var lastNameElement = await WaitForElementAsync(By.Id("userLastName")); Assert.Equal("test@example.com", emailElement.Text); Assert.Equal("Test", firstNameElement.Text); Assert.Equal("User", lastNameElement.Text); WriteLine("User details E2E test passed"); } [Fact] public async Task UpdateUser_ShouldUpdateAndDisplayChanges() { // Arrange var userId = await CreateTestUserViaApiAsync(); await NavigateToAsync($"/users/{userId}/edit"); var newFirstName = "Updated"; var newLastName = "Name"; // Act await TypeAsync(By.Id("firstName"), newFirstName); await TypeAsync(By.Id("lastName"), newLastName); await ClickAsync(By.Id("updateButton")); // Assert var successMessage = await WaitForElementAsync(By.ClassName("success-message")); Assert.Contains("User updated successfully", successMessage.Text); // Verify changes are displayed await NavigateToAsync($"/users/{userId}"); var firstNameElement = await WaitForElementAsync(By.Id("userFirstName")); var lastNameElement = await WaitForElementAsync(By.Id("userLastName")); Assert.Equal(newFirstName, firstNameElement.Text); Assert.Equal(newLastName, lastNameElement.Text); WriteLine("User update E2E test passed"); } private async Task<Guid> CreateTestUserViaApiAsync() { using var client = new HttpClient(); var request = new CreateUserRequest { Email = "test@example.com", FirstName = "Test", LastName = "User" }; var json = JsonSerializer.Serialize(request); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await client.PostAsync($"{_baseUrl}/api/users", content); var responseContent = await response.Content.ReadAsStringAsync(); var user = JsonSerializer.Deserialize<UserDto>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); return user!.Id; } }

๐Ÿ“ Summary

This guide covered comprehensive testing strategies for C# TuskLang applications:

- Unit Testing: Test base classes, service tests, and configuration tests with mocking - Integration Testing: API integration tests with test servers and service overrides - End-to-End Testing: Browser automation tests for complete user workflows - Testing Best Practices: Proper test organization, mocking strategies, and assertions

These testing frameworks ensure your C# TuskLang applications are thoroughly tested and maintain high code quality throughout development.