🧩 Components Guide

Components are the building blocks of Accelergreat. They represent your test dependencies (databases, APIs, services) and handle all the heavy lifting automatically.

New to Accelergreat? Read the main documentation first.

🚀 Quick Overview

// Components represent your test dependencies
public class OrderDatabaseComponent : SqlServerEntityFrameworkDatabaseComponent<OrderDbContext> { }
public class OrderApiComponent : WebAppComponent<OrderApi.Program> { }
public class PaymentServiceComponent : KestrelWebAppComponent<PaymentService.Program> { }

What components do for you:

  • Auto-initialization - Set up before tests run
  • Lightning-fast resets - Clean state between tests (0-3ms!)
  • Parallel execution - Scale across multiple environments
  • Auto-cleanup - Dispose resources after tests complete

🗄️ Database Components

SQL Server - Lightning Fast

public class ProductDatabaseComponent : SqlServerEntityFrameworkDatabaseComponent<ProductDbContext>
{
    public ProductDatabaseComponent(IConfiguration configuration) : base(configuration)
    {
    }

    // Add global test data (persists across all tests)
    protected override async Task OnDatabaseInitializedAsync(ProductDbContext context)
    {
        context.Categories.AddRange(
            new Category { Name = "Electronics", IsActive = true },
            new Category { Name = "Books", IsActive = true }
        );
        await context.SaveChangesAsync();
    }

    // Called after each test reset (optional)
    protected override async Task OnDatabaseResetAsync(ProductDbContext context)
    {
        // Re-seed data that doesn't survive resets
        await RefreshCache(context);
    }
}

Configuration Options

// accelergreat.development.json
{
  "SqlServerEntityFramework": {
    "ResetStrategy": "Transactions",        // 0-3ms resets!
    "CreateStrategy": "Migrations",         // Use EF migrations
    "ConnectionString": "Server=localhost;Database=TestDb;Integrated Security=true;"
  }
}

// accelergreat.ci.json
{
  "SqlServerEntityFramework": {
    "ResetStrategy": "SnapshotRollback",    // 80-150ms resets
    "CreateStrategy": "TypeConfigurations", // Use EF model
    "ConnectionString": "Server=ci-server;Database=TestDb;User Id=testuser;Password=testpass;"
  }
}

Reset Strategies Comparison

Strategy Speed Reliability Best For
Transactions 0-3ms High Development, fast feedback
SnapshotRollback 🔄 80-150ms Very High CI/CD, production-like

SQLite - Ultra Fast

public class FastDatabaseComponent : SqliteEntityFrameworkDatabaseComponent<FastDbContext>
{
    public FastDatabaseComponent(IConfiguration configuration) : base(configuration)
    {
    }
}

📋 Note: PostgreSQL and In-Memory providers are planned for future releases. Currently supported: SQL Server and SQLite.


🌐 Web Application Components

Modern .NET APIs

public class OrderApiComponent : WebAppComponent<Program>
{
    protected override void BuildConfiguration(
        IConfigurationBuilder configurationBuilder,
        IReadOnlyAccelergreatEnvironmentPipelineData environmentData)
    {
        // Auto-inject database connection
        configurationBuilder.AddEntityFrameworkDatabaseConnectionString<OrderDbContext>(
            "DefaultConnection", environmentData);
            
        // Add custom configuration
        configurationBuilder.AddInMemoryCollection(new[]
        {
            new KeyValuePair<string, string>("ApiKey", "test-key-123"),
            new KeyValuePair<string, string>("Environment", "Testing")
        });
    }

    protected override void ConfigureWebHost(
        IWebHostBuilder builder, 
        IConfiguration configuration,
        IReadOnlyAccelergreatEnvironmentPipelineData environmentData)
    {
        builder.UseEnvironment("Testing");
        
        // Configure services for testing
        builder.ConfigureServices(services =>
        {
            services.AddAccelergreatDbContext<OrderDbContext>(
                environmentData, 
                useTransactionOverriding: true  // Handle nested transactions
            );
        });
    }
}

Legacy Startup.cs APIs

public class LegacyApiComponent : WebAppComponent<LegacyApi.Startup>
{
    protected override void BuildConfiguration(
        IConfigurationBuilder configurationBuilder,
        IReadOnlyAccelergreatEnvironmentPipelineData environmentData)
    {
        configurationBuilder.AddEntityFrameworkDatabaseConnectionString<LegacyDbContext>(
            "DefaultConnection", environmentData);
    }
}

Kestrel Components (Microservices)

public class PaymentServiceComponent : KestrelWebAppComponent<PaymentService.Program>
{
    protected override void BuildConfiguration(
        IConfigurationBuilder configurationBuilder,
        IReadOnlyAccelergreatEnvironmentPipelineData environmentData)
    {
        // Get other service URLs
        var orderServiceUrl = environmentData.GetKestrelWebAppHttpBaseAddress<OrderService.Program>();
        
        configurationBuilder.AddInMemoryCollection(new[]
        {
            new KeyValuePair<string, string>("OrderService:BaseUrl", orderServiceUrl),
            new KeyValuePair<string, string>("PaymentProvider:ApiKey", "test-key")
        });
    }
}

🔧 Advanced Features

Transaction Overriding (Premium)

Handle nested transactions in your application:

builder.ConfigureServices(services =>
{
    // Enable transaction overriding
    services.AddAccelergreatDbContext<OrderDbContext>(
        environmentData,
        useTransactionOverriding: true
    );
});

What it does:

  • Intercepts BeginTransaction() calls
  • Redirects to savepoints instead of new transactions
  • Handles Commit() and Rollback() automatically

Database Provider-Specific Extensions

// SQL Server specific
services.AddSqlServerDbContextUsingTransaction<OrderDbContext>(
    environmentData,
    options => options.EnableRetryOnFailure(),
    useTransactionOverriding: true
);

// Universal (works with any provider)
services.AddAccelergreatDbContext<OrderDbContext>(
    environmentData,
    useTransactionOverriding: true
);

Global Data Management

public class ProductDatabaseComponent : SqlServerEntityFrameworkDatabaseComponent<ProductDbContext>
{
    public ProductDatabaseComponent(IConfiguration configuration) : base(configuration)
    {
        // Auto-register global data entities
        AutoRegisterGlobalDataItemsInDbContextCreation = true;
    }

    protected override async Task OnDatabaseInitializedAsync(ProductDbContext context)
    {
        // This data persists across all tests
        var categories = new[]
        {
            new Category { Name = "Electronics", IsActive = true },
            new Category { Name = "Books", IsActive = true },
            new Category { Name = "Clothing", IsActive = true }
        };
        
        context.Categories.AddRange(categories);
        await context.SaveChangesAsync();
        
        // You can also call complex setup methods
        await SeedComplexTestData(context);
    }
}

🔨 Building Custom Components

Basic Custom Component

public class RedisComponent : IAccelergreatComponent
{
    private ConnectionMultiplexer _redis;
    private IDatabase _database;

    public async Task InitializeAsync(IAccelergreatEnvironmentPipelineData environmentData)
    {
        // Connect to Redis
        _redis = await ConnectionMultiplexer.ConnectAsync("localhost:6379");
        _database = _redis.GetDatabase();
        
        // Make available to other components
        environmentData.Add("RedisConnection", _redis);
        environmentData.Add("RedisDatabase", _database);
        
        // Optional: Add test data
        await _database.StringSetAsync("test:initialized", "true");
    }

    public async Task ResetAsync()
    {
        // Clear Redis between tests
        await _redis.GetServer("localhost:6379").FlushDatabaseAsync();
        
        // Re-add any persistent test data
        await _database.StringSetAsync("test:initialized", "true");
    }

    public async ValueTask DisposeAsync()
    {
        await _redis.DisposeAsync();
    }
}

Advanced Custom Component

public class ElasticsearchComponent : IAccelergreatComponent
{
    private ElasticClient _client;
    private readonly string _indexName = "test-index";

    public async Task InitializeAsync(IAccelergreatEnvironmentPipelineData environmentData)
    {
        var settings = new ConnectionSettings(new Uri("http://localhost:9200"))
            .DefaultIndex(_indexName);
        
        _client = new ElasticClient(settings);
        
        // Create index with mapping
        await _client.Indices.CreateAsync(_indexName, c => c
            .Map<Product>(m => m.AutoMap())
        );
        
        // Seed test data
        await SeedTestDocuments();
        
        // Share with other components
        environmentData.Add("ElasticsearchClient", _client);
    }

    public async Task ResetAsync()
    {
        // Clear and re-seed
        await _client.DeleteByQueryAsync<Product>(q => q.MatchAll());
        await SeedTestDocuments();
    }

    private async Task SeedTestDocuments()
    {
        var products = new[]
        {
            new Product { Id = 1, Name = "Test Product 1", Category = "Electronics" },
            new Product { Id = 2, Name = "Test Product 2", Category = "Books" }
        };
        
        await _client.IndexManyAsync(products);
        await _client.Indices.RefreshAsync(_indexName);
    }

    public async ValueTask DisposeAsync()
    {
        await _client.Indices.DeleteAsync(_indexName);
    }
}

Docker Container Component

public class DockerDatabaseComponent : IAccelergreatComponent
{
    private string _containerId;
    private readonly string _connectionString;

    public async Task InitializeAsync(IAccelergreatEnvironmentPipelineData environmentData)
    {
        // Start PostgreSQL container
        var startInfo = new ProcessStartInfo
        {
            FileName = "docker",
            Arguments = "run -d -p 5432:5432 -e POSTGRES_PASSWORD=testpass postgres:13",
            UseShellExecute = false,
            RedirectStandardOutput = true
        };
        
        using var process = Process.Start(startInfo);
        _containerId = (await process.StandardOutput.ReadToEndAsync()).Trim();
        
        // Wait for container to be ready
        await WaitForDatabase();
        
        // Share connection string
        environmentData.Add("DockerDbConnectionString", _connectionString);
    }

    public async Task ResetAsync()
    {
        // Reset database by recreating it
        await ExecuteDockerCommand($"exec {_containerId} dropdb -U postgres testdb");
        await ExecuteDockerCommand($"exec {_containerId} createdb -U postgres testdb");
    }

    public async ValueTask DisposeAsync()
    {
        await ExecuteDockerCommand($"stop {_containerId}");
        await ExecuteDockerCommand($"rm {_containerId}");
    }
}

🎯 Best Practices

1. Component Ordering

// ✅ Correct order - dependencies first
public void Configure(IAccelergreatBuilder builder)
{
    builder.AddAccelergreatComponent<DatabaseComponent>();      // Infrastructure
    builder.AddAccelergreatComponent<CacheComponent>();         // Depends on DB
    builder.AddAccelergreatComponent<ApiComponent>();           // Depends on DB + Cache
    builder.AddAccelergreatComponent<ExternalServiceComponent>(); // Depends on API
}

2. Use Appropriate Lifetimes

// ✅ Good choices
builder.AddAccelergreatComponent<DatabaseComponent>();          // Transient - needs isolation
builder.AddAccelergreatComponent<ApiComponent>();               // Transient - needs fresh state
builder.AddSingletonAccelergreatComponent<ExternalServiceComponent>(); // Singleton - expensive to create

// ❌ Poor choices
builder.AddSingletonAccelergreatComponent<DatabaseComponent>(); // Bad - shared state between tests
builder.AddAccelergreatComponent<ExternalServiceComponent>();   // Bad - unnecessary overhead

3. Environment-Specific Configuration

// Development: Fast feedback
{
  "SqlServerEntityFramework": { "ResetStrategy": "Transactions" }
}

// CI: Reliability
{
  "SqlServerEntityFramework": { "ResetStrategy": "SnapshotRollback" }
}

// Integration: Production-like
{
  "SqlServerEntityFramework": { 
    "ResetStrategy": "SnapshotRollback",
    "ConnectionString": "Server=integration-server;..."
  }
}

4. Component Communication

public class ApiComponent : WebAppComponent<Program>
{
    protected override void BuildConfiguration(
        IConfigurationBuilder configurationBuilder,
        IReadOnlyAccelergreatEnvironmentPipelineData environmentData)
    {
        // ✅ Get data from other components
        var redisConnection = environmentData.Get<ConnectionMultiplexer>("RedisConnection");
        var dbConnectionString = environmentData.GetEntityFrameworkDatabaseConnectionString<AppDbContext>();
        
        configurationBuilder.AddInMemoryCollection(new[]
        {
            new KeyValuePair<string, string>("ConnectionStrings:DefaultConnection", dbConnectionString),
            new KeyValuePair<string, string>("Redis:ConnectionString", redisConnection.ToString())
        });
    }
}

🚀 Performance Optimization

1. Use Transactions for Development

{
  "SqlServerEntityFramework": {
    "ResetStrategy": "Transactions"  // 0-3ms resets!
  }
}

2. Leverage Parallel Execution

// Group tests in collections for parallel execution
[Collection("OrderTests")]
public class OrderCreationTests : AccelergreatXunitTest { }

[Collection("OrderTests")]
public class OrderUpdateTests : AccelergreatXunitTest { }

[Collection("ProductTests")]  // Runs in parallel with OrderTests
public class ProductTests : AccelergreatXunitTest { }

3. Optimize Component Initialization

public class OptimizedDatabaseComponent : SqlServerEntityFrameworkDatabaseComponent<DbContext>
{
    protected override async Task OnDatabaseInitializedAsync(DbContext context)
    {
        // ✅ Bulk operations
        context.Products.AddRange(GenerateTestProducts(1000));
        await context.SaveChangesAsync();
        
        // ❌ Individual operations
        // foreach (var product in products)
        // {
        //     context.Products.Add(product);
        //     await context.SaveChangesAsync();
        // }
    }
}

📊 Monitoring & Diagnostics

Component Performance Metrics

// Accelergreat automatically logs:
// - Component initialization time
// - Reset operation duration
// - Environment allocation/deallocation
// - Parallel execution statistics

Example Log Output

[INFO] Initialized environment [1] in 234ms
[INFO] Environment [1] allocated.
[INFO] Reset environment [1] in 2ms.
[INFO] Environment [2] allocated.
[INFO] Initialized 4 environments in 1.2s

📚 Next Steps


📝 Documentation Notice: This documentation has been generated using Cursor AI to improve clarity and developer experience. While every effort has been made to proofread and ensure accuracy, if you encounter any issues or inaccuracies, please contact us at mail@accelergreat.net.

Made with ❤️ by developers, for developers. Components make your integration tests fast, reliable, and maintainable.