Web Automation Framework
A production-ready web automation framework built with C# and Microsoft Playwright for enterprise-grade testing and process automation.
Web Automation Framework
A production-ready web automation framework built with C# and Microsoft Playwright, designed for scalable testing and process automation across modern web applications. This framework provides a robust foundation for enterprise-grade test automation with comprehensive reporting, parallel execution, and CI/CD integration.
Framework Overview
This framework implements industry best practices including Page Object Model, dependency injection, configuration management, and comprehensive logging to ensure maintainable and reliable automation solutions.
Key Features
- Cross-browser automation with Playwright (Chromium, Firefox, Safari)
- Page Object Model design pattern with dependency injection
- Parallel test execution with thread-safe operations
- Automatic screenshot and video capture on failures
- Comprehensive logging and detailed reporting
- Configuration management with environment-specific settings
- CI/CD integration with GitHub Actions and Azure DevOps
- Built-in retry mechanisms and flaky test handling
- API testing integration alongside UI automation
- Docker containerization for consistent execution environments
Architecture Overview
Architecture Overview
The framework architecture shows the flow from test runner through configuration and browser management to page objects and reporting, ensuring clean separation of concerns.
Technology Stack
- **Framework**: .NET 8 with C# 12 features
- **Automation**: Microsoft Playwright for cross-browser support
- **Testing**: xUnit with custom fixtures and collections
- **DI Container**: Microsoft.Extensions.DependencyInjection
- **Configuration**: Microsoft.Extensions.Configuration with appsettings.json
- **Logging**: Serilog with structured logging
- **Reporting**: Allure Framework with custom extensions
- **Build**: MSBuild with NuGet package management
- **CI/CD**: GitHub Actions and Azure DevOps pipelines
Implementation Details
The framework is built with enterprise-grade patterns and practices to ensure scalability, maintainability, and reliability in complex testing scenarios.
Configuration Management
Environment-specific configurations with secure credential management and feature toggles for different test scenarios.
Browser Management
Centralized browser factory with support for multiple browsers, headless execution, and custom launch options.
Page Object Pattern
Structured page objects with base classes, common actions, and reusable components for maintainable test code.
Test Execution
Parallel test execution with thread-safe operations, automatic retries, and comprehensive error handling.
Code Implementation
// Base Page Object with Common Functionality
using Microsoft.Playwright;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
public abstract class BasePage
{
protected readonly IPage Page;
protected readonly ILogger Logger;
protected readonly IConfiguration Config;
protected BasePage(IPage page, ILogger logger, IConfiguration config)
{
Page = page;
Logger = logger;
Config = config;
}
protected async Task<bool> WaitForElementAsync(string selector, int timeoutMs = 30000)
{
try
{
await Page.WaitForSelectorAsync(selector, new() { Timeout = timeoutMs });
Logger.LogInformation("Element found: {Selector}", selector);
return true;
}
catch (TimeoutException)
{
Logger.LogWarning("Element not found within timeout: {Selector}", selector);
return false;
}
}
protected async Task<string> TakeScreenshotAsync(string testName)
{
var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
var fileName = $"{testName}_{timestamp}.png";
var path = Path.Combine("Screenshots", fileName);
await Page.ScreenshotAsync(new() { Path = path, FullPage = true });
Logger.LogInformation("Screenshot saved: {Path}", path);
return path;
}
}
// Example Page Object Implementation
public class LoginPage : BasePage
{
private readonly string UsernameSelector = "[data-testid='username']";
private readonly string PasswordSelector = "[data-testid='password']";
private readonly string LoginButtonSelector = "[data-testid='login-button']";
private readonly string ErrorMessageSelector = "[data-testid='error-message']";
public LoginPage(IPage page, ILogger<LoginPage> logger, IConfiguration config)
: base(page, logger, config) { }
public async Task NavigateAsync()
{
var baseUrl = Config["TestSettings:BaseUrl"];
await Page.GotoAsync($"{baseUrl}/login");
Logger.LogInformation("Navigated to login page");
}
public async Task<bool> LoginAsync(string username, string password)
{
try
{
await Page.FillAsync(UsernameSelector, username);
await Page.FillAsync(PasswordSelector, password);
await Page.ClickAsync(LoginButtonSelector);
// Wait for navigation or error
await Page.WaitForLoadStateAsync(LoadState.NetworkIdle);
// Check if login was successful
var isErrorVisible = await Page.IsVisibleAsync(ErrorMessageSelector);
if (isErrorVisible)
{
var errorText = await Page.TextContentAsync(ErrorMessageSelector);
Logger.LogWarning("Login failed: {Error}", errorText);
return false;
}
Logger.LogInformation("Login successful for user: {Username}", username);
return true;
}
catch (Exception ex)
{
Logger.LogError(ex, "Error during login process");
await TakeScreenshotAsync("login_error");
throw;
}
}
}
// Test Class with Dependency Injection
[Collection("WebAutomation")]
public class LoginTests : IAsyncLifetime
{
private readonly IServiceProvider _serviceProvider;
private readonly IBrowser _browser;
private readonly ILogger<LoginTests> _logger;
private IPage _page;
private LoginPage _loginPage;
public LoginTests(WebAutomationFixture fixture)
{
_serviceProvider = fixture.ServiceProvider;
_browser = fixture.Browser;
_logger = _serviceProvider.GetRequiredService<ILogger<LoginTests>>();
}
public async Task InitializeAsync()
{
_page = await _browser.NewPageAsync();
_loginPage = _serviceProvider.GetRequiredService<LoginPage>();
// Configure page for testing
await _page.SetViewportSizeAsync(1920, 1080);
_page.SetDefaultTimeout(30000);
}
[Fact]
[Trait("Category", "Smoke")]
public async Task LoginWithValidCredentials_ShouldSucceed()
{
// Arrange
var username = "testuser@example.com";
var password = "SecurePassword123!";
// Act
await _loginPage.NavigateAsync();
var result = await _loginPage.LoginAsync(username, password);
// Assert
Assert.True(result, "Login should succeed with valid credentials");
// Verify redirect to dashboard
await _page.WaitForURLAsync("**/dashboard");
var currentUrl = _page.Url;
Assert.Contains("dashboard", currentUrl);
}
[Theory]
[InlineData("", "password", "Username is required")]
[InlineData("invalid-email", "password", "Invalid email format")]
[InlineData("user@test.com", "", "Password is required")]
public async Task LoginWithInvalidCredentials_ShouldShowError(
string username, string password, string expectedError)
{
// Act
await _loginPage.NavigateAsync();
var result = await _loginPage.LoginAsync(username, password);
// Assert
Assert.False(result, "Login should fail with invalid credentials");
}
public async Task DisposeAsync()
{
await _page?.CloseAsync();
}
}
Performance Metrics
Advanced Features
Configuration Management
{
"TestSettings": {
"BaseUrl": "https://app.example.com",
"Timeout": 30000,
"RetryCount": 3,
"BrowserType": "chromium",
"Headless": true,
"VideoRecording": true,
"SlowMo": 0
},
"Environments": {
"Development": {
"BaseUrl": "https://dev.example.com",
"Database": "Dev_TestDB"
},
"Staging": {
"BaseUrl": "https://staging.example.com",
"Database": "Staging_TestDB"
}
},
"Credentials": {
"TestUser": {
"Username": "test@example.com",
"Password": "encrypted_password"
}
}
}
CI/CD Integration
# GitHub Actions Workflow
name: Web Automation Tests
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 2 * * *' # Daily at 2 AM
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
browser: [chromium, firefox, webkit]
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Install Playwright
run: |
dotnet build
pwsh bin/Debug/net8.0/playwright.ps1 install
- name: Run Tests
run: |
dotnet test --configuration Release \\
--logger "trx;LogFileName=test-results.trx" \\
--collect:"XPlat Code Coverage" \\
-- TestSettings.BrowserType=${{ matrix.browser }}
- name: Upload Test Results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-${{ matrix.browser }}
path: |
TestResults/
Screenshots/
Videos/
- name: Generate Allure Report
if: always()
run: |
npm install -g allure-commandline
allure generate allure-results --clean -o allure-report
- name: Deploy Test Report
if: always()
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./allure-report
Use Cases & Applications
- **E-Commerce Testing**: Complete shopping workflows, payment processing, inventory management
- **SaaS Application Testing**: User onboarding, feature workflows, subscription management
- **Financial Services**: Transaction processing, compliance validation, security testing
- **Healthcare Applications**: Patient data management, appointment scheduling, regulatory compliance
- **Enterprise Applications**: Complex business workflows, integration testing, user access control
- **API + UI Integration**: Combined API and UI testing for comprehensive coverage
- **Performance Testing**: Load testing with realistic user interactions
- **Accessibility Testing**: Automated accessibility compliance validation
Getting Started
Follow these steps to set up and start using the web automation framework:
Prerequisites Checklist
Production Ready
This framework has been battle-tested in enterprise environments with thousands of automated tests running daily. It includes comprehensive error handling, retry mechanisms, and detailed reporting for production use.
Best Practices Implemented
- **Separation of Concerns**: Clear separation between page objects, test logic, and configuration
- **Dependency Injection**: Proper DI container usage for testability and maintainability
- **Error Handling**: Comprehensive exception handling with detailed logging
- **Test Data Management**: Externalized test data with environment-specific configurations
- **Parallel Execution**: Thread-safe implementation for maximum test throughput
- **Reporting**: Detailed test reports with screenshots, videos, and execution metrics
- **Code Quality**: Static analysis, code coverage, and automated quality gates
Framework Extensions
- Custom assertions and matchers for common scenarios
- Database testing utilities for data validation
- Email testing integration for notification workflows
- Mobile testing capabilities with device emulation
- Visual regression testing with image comparison
- Performance monitoring and metrics collection
Start Building Robust Test Automation
Ready to implement enterprise-grade web automation? Explore the comprehensive framework and start building reliable, maintainable test suites that scale with your application.