Integration Testing

Doing it the right way

A comprehensive guide to effective integration testing

Welcome

This presentation will guide you through best practices for integration testing

๐Ÿš€ Ready to learn?

My Story

How I came to Integration Testing

A personal journey through challenges and solutions

The Starting Point

๐Ÿ“Š Integration Testing against central infrastructure

Example: Elasticsearch

  • Central test infrastructure for all teams
  • Shared Elasticsearch instance
  • All developers use the same environment

The Success

โœ… Automated Integration Tests

Complete functionality ensured

  • Tests against central test infrastructure
  • High confidence in functionality
  • Automated validation of all features

The Problems

โš ๏ธ Increasing load and latency

  • Local Development: Every developer tests against central infrastructure
  • Pull Requests: CI/CD pipeline increases load
  • UI Tests: Frontend tests add additional load

๐Ÿšจ Result: High latencies and unstable tests

The Quest for Solutions

๐Ÿ” How can the load be reduced?

This question led to a comprehensive analysis of integration testing strategies...

โ†’ That's the reason for this presentation!

What We'll Cover

  • Understanding Integration Testing
  • Best Practices & Strategies
  • Tools & Frameworks
  • Real-world Examples
  • Common Pitfalls to Avoid

Testing Types Overview

Let's explore different types of testing and how they fit together

Understanding the landscape helps us make better testing decisions

Unit Testing

๐Ÿ”ฌ Definition

Testing individual components or functions in isolation

๐Ÿ“ Characteristics

  • Fast execution (milliseconds)
  • No external dependencies
  • High test coverage possible
  • Easy to debug failures

โœ… When to Use

Business logic, algorithms, data transformations, utility functions

Integration Testing

๐Ÿ”— Definition

Testing the interaction between integrated components or services

๐Ÿ“ Characteristics

  • Moderate execution time (seconds)
  • Tests component boundaries
  • Verifies data flow between modules
  • Can use test doubles or real dependencies

โœ… When to Use

API integrations, database interactions, message queues, service boundaries

System Testing

๐Ÿ—๏ธ Definition

Testing the complete integrated system against requirements

๐Ÿ“ Characteristics

  • Tests entire application flow
  • Production-like environment
  • All components working together
  • Performance and reliability focus

โœ… When to Use

Complete workflows, system requirements validation, performance testing

End-to-End Testing

๐ŸŒ Definition

Testing complete user scenarios from start to finish

๐Ÿ“ Characteristics

  • User perspective testing
  • Real environment simulation
  • Cross-system interactions
  • Longest execution time

โœ… When to Use

Critical user journeys, business process validation, acceptance testing

UI Testing

๐ŸŽจ Definition

Testing user interface behavior and visual elements

๐Ÿ“ Characteristics

  • Browser/device specific
  • Visual and interaction testing
  • Can be brittle and slow
  • Covers user experience

โœ… When to Use

User interface flows, accessibility, responsive design, visual regression

The Testing Pyramid

๐Ÿ”๏ธ Classic Approach

Testing Pyramid

๐Ÿ’ก Key Principles

  • Many Unit Tests: Fast, cheap, reliable foundation
  • Some Integration Tests: Verify component interactions
  • Few E2E Tests: Cover critical user journeys

The Testing Diamond

๐Ÿ’Ž Modern Alternative

Testing Diamond

๐ŸŽฏ Focus Areas

  • Integration Tests: Primary focus for microservices
  • Contract Testing: Service boundary verification
  • Balanced Approach: Adapts to architecture

Alternative Approaches

๐Ÿ”„ Testing Trophy

Emphasizes integration tests for web applications

โš–๏ธ Testing Honeycomb

Flexible approach based on context and risk

๐ŸŽฏ Key Insight

Choose the approach that fits your architecture, team, and risk profile

Integration Testing in Context

๐ŸŽฏ Where Integration Testing Shines

  • Microservices: Service-to-service communication
  • APIs: Contract validation and data flow
  • Databases: Data persistence and retrieval
  • Third-party Services: External integrations

โšก Benefits

  • Catches interface mismatches
  • Validates assumptions between components
  • Faster feedback than E2E tests
  • More realistic than unit tests

TestContainers.NET

๐Ÿณ What is TestContainers.NET?

A .NET library that provides lightweight, throwaway instances of common databases, message brokers, and other services wrapped in Docker containers.

โœจ Key Benefits

  • Isolation: Each test gets a fresh environment
  • Consistency: Same environment across all machines
  • Realistic: Tests against real dependencies

Setting Up TestContainers.NET

๐Ÿ“ฆ 1. Install NuGet Package


# Core package
dotnet add package Testcontainers

# Database-specific packages
dotnet add package Testcontainers.PostgreSql
dotnet add package Testcontainers.MsSql
dotnet add package Testcontainers.Redis
                    

โš™๏ธ 2. Basic Configuration

TestContainers requires Docker to be running on your machine. It automatically manages container lifecycle during tests.

Code Example

๐Ÿงช PostgreSQL Integration Test


[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));
}
                    

Advanced Example

๐Ÿ—๏ธ Test Fixture with Setup


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() { /* ... */ }
}
                    

Key Considerations

โšก Build Agent Resources

  • โ€ข Ensure Docker is available on CI/CD agents
  • โ€ข Allocate sufficient memory (containers + tests)
  • โ€ข Consider parallel test execution limits
  • โ€ข Monitor disk space for container images

๐Ÿณ Docker Environment

  • โ€ข Use lightweight images (alpine variants)
  • โ€ข Configure wait strategies for container readiness
  • โ€ข Set appropriate timeouts for slow environments
  • โ€ข Clean up containers after test completion

TestContainers.NET Best Practices

๐Ÿ’ก Optimization Tips

  • Share Containers: Use test fixtures for expensive setups
  • Fast Images: Prefer Alpine or minimal base images
  • Wait Strategies: Implement proper readiness checks
  • Cleanup: Always dispose containers properly

โœ… Result: Isolated, reliable, and maintainable integration tests

Best Practices

๐ŸŽฏ Strategic Guidelines

  • Start with Unit Tests: Build a solid foundation
  • Focus on Integration: Test the boundaries that matter
  • Minimize E2E: Only for critical user journeys
  • Adapt to Context: Architecture drives testing strategy

๐Ÿ’ก Remember

The goal is confidence in your system, not perfect test coverage

Thank You!

Questions & Discussion