A comprehensive guide to effective integration testing
This presentation will guide you through best practices for integration testing
๐ Ready to learn?
A personal journey through challenges and solutions
Example: Elasticsearch
Complete functionality ensured
๐จ Result: High latencies and unstable tests
This question led to a comprehensive analysis of integration testing strategies...
โ That's the reason for this presentation!
Let's explore different types of testing and how they fit together
Understanding the landscape helps us make better testing decisions
Testing individual components or functions in isolation
Business logic, algorithms, data transformations, utility functions
Testing the interaction between integrated components or services
API integrations, database interactions, message queues, service boundaries
Testing the complete integrated system against requirements
Complete workflows, system requirements validation, performance testing
Testing complete user scenarios from start to finish
Critical user journeys, business process validation, acceptance testing
Testing user interface behavior and visual elements
User interface flows, accessibility, responsive design, visual regression
Emphasizes integration tests for web applications
Flexible approach based on context and risk
Choose the approach that fits your architecture, team, and risk profile
A .NET library that provides lightweight, throwaway instances of common databases, message brokers, and other services wrapped in Docker containers.
# Core package
dotnet add package Testcontainers
# Database-specific packages
dotnet add package Testcontainers.PostgreSql
dotnet add package Testcontainers.MsSql
dotnet add package Testcontainers.Redis
TestContainers requires Docker to be running on your machine. It automatically manages container lifecycle during tests.
[Test]
public async Task DatabaseIntegrationTest()
{
// Arrange
await using var container = new PostgreSqlBuilder()
.WithImage("postgres:15-alpine")
.WithDatabase("testdb")
.WithUsername("testuser")
.WithPassword("testpass")
.WithPortBinding(5432, true)
.Build();
await container.StartAsync();
var connectionString = container.GetConnectionString();
// Act
await using var connection = new NpgsqlConnection(connectionString);
await connection.OpenAsync();
var command = new NpgsqlCommand("SELECT 1", connection);
var result = await command.ExecuteScalarAsync();
// Assert
Assert.That(result, Is.EqualTo(1));
}
public class DatabaseTestFixture : IAsyncLifetime
{
private PostgreSqlContainer _container;
public string ConnectionString { get; private set; }
public async Task InitializeAsync()
{
_container = new PostgreSqlBuilder()
.WithImage("postgres:15-alpine")
.WithDatabase("testdb")
.WithUsername("testuser")
.WithPassword("testpass")
.WithWaitStrategy(Wait.ForUnixContainer()
.UntilPortIsAvailable(5432))
.Build();
await _container.StartAsync();
ConnectionString = _container.GetConnectionString();
// Run database migrations
await RunMigrations();
}
public async Task DisposeAsync()
{
await _container.DisposeAsync();
}
private async Task RunMigrations() { /* ... */ }
}
โ Result: Isolated, reliable, and maintainable integration tests
The goal is confidence in your system, not perfect test coverage
Questions & Discussion