Skip to content

Testing Strategies

Overview

This section covers comprehensive testing strategies for applications using the Flaggy feature flag library, including unit tests, integration tests, and testing patterns for feature flag-driven code.

Test Setup

Using InMemory Provider for Tests

The InMemory provider is ideal for testing as it provides fast, isolated flag storage without external dependencies.

using Flaggy.Abstractions;
using Flaggy.Extensions;
using Flaggy.Models;
using Flaggy.Providers;
using Microsoft.Extensions.DependencyInjection;
using Xunit;

public class FeatureFlagTestBase : IDisposable
{
    protected readonly IServiceProvider ServiceProvider;
    protected readonly IFeatureFlagService FlagService;

    public FeatureFlagTestBase()
    {
        var services = new ServiceCollection();

        // Use InMemory provider for testing
        services.AddFlaggy(new InMemoryFeatureFlagProvider());

        ServiceProvider = services.BuildServiceProvider();
        FlagService = ServiceProvider.GetRequiredService<IFeatureFlagService>();
    }

    public void Dispose()
    {
        (ServiceProvider as IDisposable)?.Dispose();
    }
}

Test Fixtures with Pre-seeded Flags

public class FeatureFlagFixture : IAsyncLifetime
{
    public IFeatureFlagService FlagService { get; private set; }

    public async Task InitializeAsync()
    {
        var services = new ServiceCollection();
        services.AddFlaggy(new InMemoryFeatureFlagProvider());

        var provider = services.BuildServiceProvider();
        FlagService = provider.GetRequiredService<IFeatureFlagService>();

        // Pre-seed common test flags
        await FlagService.CreateFlagAsync(new FeatureFlag
        {
            Key = "test-feature",
            is_enabled = true,
            description = "Test feature flag"
        });

        await FlagService.CreateFlagAsync(new FeatureFlag
        {
            Key = "disabled-feature",
            is_enabled = false,
            description = "Disabled test feature"
        });
    }

    public Task DisposeAsync() => Task.CompletedTask;
}

public class MyTests : IClassFixture<FeatureFlagFixture>
{
    private readonly IFeatureFlagService _flagService;

    public MyTests(FeatureFlagFixture fixture)
    {
        _flagService = fixture.FlagService;
    }

    [Fact]
    public async Task TestFeature_is_enabled()
    {
        var isEnabled = await _flagService.is_enabledAsync("test-feature");
        Assert.True(isEnabled);
    }
}

Unit Testing Patterns

Testing Flag Evaluation

public class FlagEvaluationTests : FeatureFlagTestBase
{
    [Fact]
    public async Task is_enabledAsync_WhenFlagExists_ReturnsCorrectState()
    {
        // Arrange
        await FlagService.CreateFlagAsync(new FeatureFlag
        {
            Key = "my-feature",
            is_enabled = true
        });

        // Act
        var isEnabled = await FlagService.is_enabledAsync("my-feature");

        // Assert
        Assert.True(isEnabled);
    }

    [Fact]
    public async Task is_enabledAsync_WhenFlagDoesNotExist_ReturnsFalse()
    {
        // Act
        var isEnabled = await FlagService.is_enabledAsync("non-existent-flag");

        // Assert
        Assert.False(isEnabled);
    }

    [Fact]
    public async Task is_enabledAsync_WhenFlagDisabled_ReturnsFalse()
    {
        // Arrange
        await FlagService.CreateFlagAsync(new FeatureFlag
        {
            Key = "disabled-feature",
            is_enabled = false
        });

        // Act
        var isEnabled = await FlagService.is_enabledAsync("disabled-feature");

        // Assert
        Assert.False(isEnabled);
    }
}

Testing Flag Values

public class FlagValueTests : FeatureFlagTestBase
{
    [Fact]
    public async Task GetValueAsync_WhenFlagHasValue_ReturnsValue()
    {
        // Arrange
        await FlagService.CreateFlagAsync(new FeatureFlag
        {
            Key = "api-endpoint",
            is_enabled = true,
            Value = "https://api.example.com"
        });

        // Act
        var value = await FlagService.GetValueAsync("api-endpoint");

        // Assert
        Assert.Equal("https://api.example.com", value);
    }

    [Fact]
    public async Task GetValueAsync_WhenFlagDisabled_ReturnsDefault()
    {
        // Arrange
        await FlagService.CreateFlagAsync(new FeatureFlag
        {
            Key = "api-endpoint",
            is_enabled = false,
            Value = "https://api.example.com"
        });

        // Act
        var value = await FlagService.GetValueAsync("api-endpoint", defaultValue: "https://default.com");

        // Assert
        Assert.Equal("https://default.com", value);
    }

    [Theory]
    [InlineData("100", 100)]
    [InlineData("0", 0)]
    [InlineData("-50", -50)]
    public async Task GetValueAsync_Int_ParsesCorrectly(string flagValue, int expected)
    {
        // Arrange
        await FlagService.CreateFlagAsync(new FeatureFlag
        {
            Key = "max-retries",
            is_enabled = true,
            Value = flagValue
        });

        // Act
        var value = await FlagService.GetValueAsync<int>("max-retries", defaultValue: 0);

        // Assert
        Assert.Equal(expected, value.Value);
    }

    [Theory]
    [InlineData("0.5", 0.5)]
    [InlineData("1.25", 1.25)]
    [InlineData("99.99", 99.99)]
    public async Task GetValueAsync_Double_ParsesCorrectly(string flagValue, double expected)
    {
        // Arrange
        await FlagService.CreateFlagAsync(new FeatureFlag
        {
            Key = "discount-rate",
            is_enabled = true,
            Value = flagValue
        });

        // Act
        var value = await FlagService.GetValueAsync<double>("discount-rate", defaultValue: 0.0);

        // Assert
        Assert.Equal(expected, value.Value, precision: 2);
    }

    [Fact]
    public async Task GetValueAsync_InvalidFormat_ReturnsDefault()
    {
        // Arrange
        await FlagService.CreateFlagAsync(new FeatureFlag
        {
            Key = "invalid-number",
            is_enabled = true,
            Value = "not-a-number"
        });

        // Act
        var value = await FlagService.GetValueAsync<int>("invalid-number", defaultValue: 42);

        // Assert
        Assert.Equal(42, value.Value);
    }
}

Testing CRUD Operations

public class FlagCrudTests : FeatureFlagTestBase
{
    [Fact]
    public async Task CreateFlagAsync_WhenValid_CreatesFlag()
    {
        // Arrange
        var flag = new FeatureFlag
        {
            Key = "new-feature",
            is_enabled = true,
            Value = "test-value",
            description = "Test description"
        };

        // Act
        var result = await FlagService.CreateFlagAsync(flag);

        // Assert
        Assert.True(result);

        var retrieved = await FlagService.GetFlagAsync("new-feature");
        Assert.NotNull(retrieved);
        Assert.Equal("test-value", retrieved.Value);
        Assert.Equal("Test description", retrieved.description);
    }

    [Fact]
    public async Task UpdateFlagAsync_WhenExists_UpdatesFlag()
    {
        // Arrange
        await FlagService.CreateFlagAsync(new FeatureFlag
        {
            Key = "existing-flag",
            is_enabled = false,
            Value = "old-value"
        });

        var updatedFlag = new FeatureFlag
        {
            Key = "existing-flag",
            is_enabled = true,
            Value = "new-value"
        };

        // Act
        var result = await FlagService.UpdateFlagAsync(updatedFlag);

        // Assert
        Assert.True(result);

        var retrieved = await FlagService.GetFlagAsync("existing-flag");
        Assert.True(retrieved.is_enabled);
        Assert.Equal("new-value", retrieved.Value);
    }

    [Fact]
    public async Task DeleteFlagAsync_WhenExists_DeletesFlag()
    {
        // Arrange
        await FlagService.CreateFlagAsync(new FeatureFlag
        {
            Key = "to-delete",
            is_enabled = true
        });

        // Act
        var result = await FlagService.DeleteFlagAsync("to-delete");

        // Assert
        Assert.True(result);

        var retrieved = await FlagService.GetFlagAsync("to-delete");
        Assert.Null(retrieved);
    }
}

Testing Extension Methods

public class FlagExtensionTests : FeatureFlagTestBase
{
    [Fact]
    public async Task CreateFlagIfNotExistsAsync_WhenNotExists_CreatesFlag()
    {
        // Act
        var result = await FlagService.CreateFlagIfNotExistsAsync(
            "new-flag",
            isEnabled: true,
            value: "test"
        );

        // Assert
        Assert.True(result);
        var flag = await FlagService.GetFlagAsync("new-flag");
        Assert.NotNull(flag);
    }

    [Fact]
    public async Task CreateFlagIfNotExistsAsync_WhenExists_DoesNotCreate()
    {
        // Arrange
        await FlagService.CreateFlagAsync(new FeatureFlag
        {
            Key = "existing",
            is_enabled = false,
            Value = "original"
        });

        // Act
        var result = await FlagService.CreateFlagIfNotExistsAsync(
            "existing",
            isEnabled: true,
            value: "new"
        );

        // Assert
        Assert.False(result);
        var flag = await FlagService.GetFlagAsync("existing");
        Assert.Equal("original", flag.Value); // Original value unchanged
    }

    [Fact]
    public async Task ToggleFlagAsync_TogglesState()
    {
        // Arrange
        await FlagService.CreateFlagAsync(new FeatureFlag
        {
            Key = "toggle-test",
            is_enabled = false
        });

        // Act
        await FlagService.ToggleFlagAsync("toggle-test");

        // Assert
        var isEnabled = await FlagService.is_enabledAsync("toggle-test");
        Assert.True(isEnabled);

        // Toggle again
        await FlagService.ToggleFlagAsync("toggle-test");
        isEnabled = await FlagService.is_enabledAsync("toggle-test");
        Assert.False(isEnabled);
    }

    [Fact]
    public async Task GetEnabledFlagsAsync_ReturnsOnlyEnabledFlags()
    {
        // Arrange
        await FlagService.CreateFlagAsync(new FeatureFlag { Key = "flag1", is_enabled = true });
        await FlagService.CreateFlagAsync(new FeatureFlag { Key = "flag2", is_enabled = false });
        await FlagService.CreateFlagAsync(new FeatureFlag { Key = "flag3", is_enabled = true });

        // Act
        var enabledFlags = await FlagService.GetEnabledFlagsAsync();

        // Assert
        Assert.Equal(2, enabledFlags.Count());
        Assert.Contains(enabledFlags, f => f.Key == "flag1");
        Assert.Contains(enabledFlags, f => f.Key == "flag3");
    }
}

Testing Feature Flag-Driven Code

Testing Both Code Paths

public class PaymentService
{
    private readonly IFeatureFlagService _flagService;
    private readonly IPaymentProcessor _newProcessor;
    private readonly IPaymentProcessor _legacyProcessor;

    public PaymentService(
        IFeatureFlagService flagService,
        IPaymentProcessor newProcessor,
        IPaymentProcessor legacyProcessor)
    {
        _flagService = flagService;
        _newProcessor = newProcessor;
        _legacyProcessor = legacyProcessor;
    }

    public async Task<PaymentResult> ProcessPaymentAsync(Payment payment)
    {
        var useNewProcessor = await _flagService.is_enabledAsync("new-payment-processor");

        return useNewProcessor
            ? await _newProcessor.ProcessAsync(payment)
            : await _legacyProcessor.ProcessAsync(payment);
    }
}

public class PaymentServiceTests
{
    private readonly Mock<IFeatureFlagService> _mockFlagService;
    private readonly Mock<IPaymentProcessor> _mockNewProcessor;
    private readonly Mock<IPaymentProcessor> _mockLegacyProcessor;
    private readonly PaymentService _service;

    public PaymentServiceTests()
    {
        _mockFlagService = new Mock<IFeatureFlagService>();
        _mockNewProcessor = new Mock<IPaymentProcessor>();
        _mockLegacyProcessor = new Mock<IPaymentProcessor>();

        _service = new PaymentService(
            _mockFlagService.Object,
            _mockNewProcessor.Object,
            _mockLegacyProcessor.Object
        );
    }

    [Fact]
    public async Task ProcessPayment_WhenFlagEnabled_UsesNewProcessor()
    {
        // Arrange
        _mockFlagService
            .Setup(x => x.is_enabledAsync("new-payment-processor", default))
            .ReturnsAsync(true);

        _mockNewProcessor
            .Setup(x => x.ProcessAsync(It.IsAny<Payment>()))
            .ReturnsAsync(new PaymentResult { Success = true });

        var payment = new Payment { Amount = 100 };

        // Act
        var result = await _service.ProcessPaymentAsync(payment);

        // Assert
        Assert.True(result.Success);
        _mockNewProcessor.Verify(x => x.ProcessAsync(payment), Times.Once);
        _mockLegacyProcessor.Verify(x => x.ProcessAsync(It.IsAny<Payment>()), Times.Never);
    }

    [Fact]
    public async Task ProcessPayment_WhenFlagDisabled_UsesLegacyProcessor()
    {
        // Arrange
        _mockFlagService
            .Setup(x => x.is_enabledAsync("new-payment-processor", default))
            .ReturnsAsync(false);

        _mockLegacyProcessor
            .Setup(x => x.ProcessAsync(It.IsAny<Payment>()))
            .ReturnsAsync(new PaymentResult { Success = true });

        var payment = new Payment { Amount = 100 };

        // Act
        var result = await _service.ProcessPaymentAsync(payment);

        // Assert
        Assert.True(result.Success);
        _mockLegacyProcessor.Verify(x => x.ProcessAsync(payment), Times.Once);
        _mockNewProcessor.Verify(x => x.ProcessAsync(It.IsAny<Payment>()), Times.Never);
    }
}

Theory-Based Testing for Multiple Flag States

public class FeatureServiceTests
{
    [Theory]
    [InlineData(true, true, "Both features enabled")]
    [InlineData(true, false, "Only feature A enabled")]
    [InlineData(false, true, "Only feature B enabled")]
    [InlineData(false, false, "No features enabled")]
    public async Task ProcessData_WithVariousFlagCombinations_BehavesCorrectly(
        bool featureAEnabled,
        bool featureBEnabled,
        string expectedBehavior)
    {
        // Arrange
        var services = new ServiceCollection();
        services.AddFlaggy(new InMemoryFeatureFlagProvider());
        var provider = services.BuildServiceProvider();
        var flagService = provider.GetRequiredService<IFeatureFlagService>();

        await flagService.UpsertFlagAsync("feature-a", featureAEnabled);
        await flagService.UpsertFlagAsync("feature-b", featureBEnabled);

        var service = new FeatureService(flagService);

        // Act
        var result = await service.ProcessDataAsync();

        // Assert
        Assert.Contains(expectedBehavior, result.description);
    }
}

Testing Flag Fallback Behavior

public class ConfigurationServiceTests
{
    [Fact]
    public async Task GetMaxRetries_WhenFlagExists_ReturnsConfiguredValue()
    {
        // Arrange
        var services = new ServiceCollection();
        services.AddFlaggy(new InMemoryFeatureFlagProvider());
        var provider = services.BuildServiceProvider();
        var flagService = provider.GetRequiredService<IFeatureFlagService>();

        await flagService.CreateFlagAsync(new FeatureFlag
        {
            Key = "max-retries",
            is_enabled = true,
            Value = "5"
        });

        var configService = new ConfigurationService(flagService);

        // Act
        var maxRetries = await configService.GetMaxRetriesAsync();

        // Assert
        Assert.Equal(5, maxRetries);
    }

    [Fact]
    public async Task GetMaxRetries_WhenFlagMissing_ReturnsDefaultValue()
    {
        // Arrange
        var services = new ServiceCollection();
        services.AddFlaggy(new InMemoryFeatureFlagProvider());
        var provider = services.BuildServiceProvider();
        var flagService = provider.GetRequiredService<IFeatureFlagService>();

        var configService = new ConfigurationService(flagService);

        // Act
        var maxRetries = await configService.GetMaxRetriesAsync();

        // Assert - Should return default value (e.g., 3)
        Assert.Equal(3, maxRetries);
    }
}

Integration Testing

Testing with Real Database Providers

public class DatabaseIntegrationTests : IAsyncLifetime
{
    private readonly string _connectionString;
    private IFeatureFlagService _flagService;

    public DatabaseIntegrationTests()
    {
        // Use test database
        _connectionString = "Server=localhost;Database=flaggy_test;User=root;Password=test;";
    }

    public async Task InitializeAsync()
    {
        var services = new ServiceCollection();
        services.AddFlaggy(options =>
{
    options.UseMySQL(
        connectionString: _connectionString,
        autoMigrate: true
    );
});

        var provider = services.BuildServiceProvider();
        _flagService = provider.GetRequiredService<IFeatureFlagService>();

        // Clean database before tests
        var allFlags = await _flagService.GetAllFlagsAsync();
        foreach (var flag in allFlags)
        {
            await _flagService.DeleteFlagAsync(flag.Key);
        }
    }

    public async Task DisposeAsync()
    {
        // Cleanup
        var allFlags = await _flagService.GetAllFlagsAsync();
        foreach (var flag in allFlags)
        {
            await _flagService.DeleteFlagAsync(flag.Key);
        }
    }

    [Fact]
    public async Task CreateAndRetrieveFlag_WithDatabase_WorksCorrectly()
    {
        // Arrange
        var flag = new FeatureFlag
        {
            Key = "db-test-flag",
            is_enabled = true,
            Value = "test-value",
            description = "Integration test flag"
        };

        // Act
        await _flagService.CreateFlagAsync(flag);
        var retrieved = await _flagService.GetFlagAsync("db-test-flag");

        // Assert
        Assert.NotNull(retrieved);
        Assert.Equal("test-value", retrieved.Value);
        Assert.Equal("Integration test flag", retrieved.description);
        Assert.NotNull(retrieved.created_at);
        Assert.NotNull(retrieved.updated_at);
    }
}

Testing Cache Behavior

public class CacheBehaviorTests : IAsyncLifetime
{
    private IFeatureFlagService _flagService;
    private IFeatureFlagProvider _provider;

    public async Task InitializeAsync()
    {
        var services = new ServiceCollection();
        _provider = new InMemoryFeatureFlagProvider();

        services.AddFlaggy(
            provider: _provider,
            cachingProvider: CachingProvider.Memory,
            cacheExpiration: TimeSpan.FromSeconds(2)
        );

        var serviceProvider = services.BuildServiceProvider();
        _flagService = serviceProvider.GetRequiredService<IFeatureFlagService>();
    }

    public Task DisposeAsync() => Task.CompletedTask;

    [Fact]
    public async Task GetFlag_AfterUpdate_ReflectsChanges()
    {
        // Arrange - Create initial flag
        await _flagService.CreateFlagAsync(new FeatureFlag
        {
            Key = "test-flag",
            is_enabled = false,
            Value = "initial"
        });

        // Verify initial state
        var initial = await _flagService.GetFlagAsync("test-flag");
        Assert.False(initial.is_enabled);
        Assert.Equal("initial", initial.Value);

        // Act - Update flag
        await _flagService.UpdateFlagAsync(new FeatureFlag
        {
            Key = "test-flag",
            is_enabled = true,
            Value = "updated"
        });

        // Assert - Should reflect updated state immediately (cache invalidation)
        var updated = await _flagService.GetFlagAsync("test-flag");
        Assert.True(updated.is_enabled);
        Assert.Equal("updated", updated.Value);
    }

    [Fact]
    public async Task GetAllFlags_PopulatesCache()
    {
        // Arrange
        await _flagService.CreateFlagAsync(new FeatureFlag { Key = "flag1", is_enabled = true });
        await _flagService.CreateFlagAsync(new FeatureFlag { Key = "flag2", is_enabled = false });

        // Act - First call populates cache
        var flags1 = await _flagService.GetAllFlagsAsync();

        // Second call should use cache (verify by checking it's the same reference or fast)
        var sw = Stopwatch.StartNew();
        var flags2 = await _flagService.GetAllFlagsAsync();
        sw.Stop();

        // Assert - Should be very fast (< 5ms) from cache
        Assert.True(sw.ElapsedMilliseconds < 5);
        Assert.Equal(flags1.Count(), flags2.Count());
    }
}

Testing API Endpoints

Testing Feature-Flagged Endpoints

public class FeatureFlaggedEndpointTests : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly WebApplicationFactory<Program> _factory;

    public FeatureFlaggedEndpointTests(WebApplicationFactory<Program> factory)
    {
        _factory = factory;
    }

    [Fact]
    public async Task GetProducts_WhenNewApiEnabled_UsesNewImplementation()
    {
        // Arrange
        var client = _factory
            .WithWebHostBuilder(builder =>
            {
                builder.ConfigureServices(services =>
                {
                    // Replace flag service with test implementation
                    services.AddScoped<IFeatureFlagService>(sp =>
                    {
                        var testServices = new ServiceCollection();
                        testServices.AddFlaggy(new InMemoryFeatureFlagProvider());
                        var provider = testServices.BuildServiceProvider();
                        var flagService = provider.GetRequiredService<IFeatureFlagService>();

                        // Enable the flag for this test
                        flagService.CreateFlagAsync(new FeatureFlag
                        {
                            Key = "use-new-product-api",
                            is_enabled = true
                        }).Wait();

                        return flagService;
                    });
                });
            })
            .CreateClient();

        // Act
        var response = await client.GetAsync("/api/products");

        // Assert
        response.EnsureSuccessStatusCode();
        var content = await response.Content.ReadAsStringAsync();
        Assert.Contains("new-api", content.ToLower());
    }
}

Test Helpers and Utilities

Flag Builder for Tests

public class FeatureFlagBuilder
{
    private string _key = "test-flag";
    private bool _isEnabled = false;
    private string? _value = null;
    private string? _description = null;

    public FeatureFlagBuilder WithKey(string key)
    {
        _key = key;
        return this;
    }

    public FeatureFlagBuilder Enabled()
    {
        _isEnabled = true;
        return this;
    }

    public FeatureFlagBuilder Disabled()
    {
        _isEnabled = false;
        return this;
    }

    public FeatureFlagBuilder WithValue(string value)
    {
        _value = value;
        return this;
    }

    public FeatureFlagBuilder Withdescription(string description)
    {
        _description = description;
        return this;
    }

    public FeatureFlag Build()
    {
        return new FeatureFlag
        {
            Key = _key,
            is_enabled = _isEnabled,
            Value = _value,
            description = _description
        };
    }

    public async Task<FeatureFlag> CreateAsync(IFeatureFlagService service)
    {
        var flag = Build();
        await service.CreateFlagAsync(flag);
        return flag;
    }
}

// Usage
var flag = await new FeatureFlagBuilder()
    .WithKey("premium-feature")
    .Enabled()
    .WithValue("tier-2")
    .Withdescription("Premium tier 2 feature")
    .CreateAsync(flagService);

Mock Flag Service

public class MockFeatureFlagService : IFeatureFlagService
{
    private readonly Dictionary<string, FeatureFlag> _flags = new();

    public Task<bool> is_enabledAsync(string key, CancellationToken ct = default)
    {
        return Task.FromResult(_flags.TryGetValue(key, out var flag) && flag.is_enabled);
    }

    public Task<string?> GetValueAsync(string key, string? defaultValue = null, CancellationToken ct = default)
    {
        if (_flags.TryGetValue(key, out var flag) && flag.is_enabled)
            return Task.FromResult(flag.Value ?? defaultValue);
        return Task.FromResult(defaultValue);
    }

    public Task<T?> GetValueAsync<T>(string key, T? defaultValue = null, CancellationToken ct = default) where T : struct
    {
        var value = GetValueAsync(key, null, ct).Result;
        if (string.IsNullOrEmpty(value))
            return Task.FromResult(defaultValue);

        try
        {
            var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T));
            return Task.FromResult((T?)converter.ConvertFromString(value));
        }
        catch
        {
            return Task.FromResult(defaultValue);
        }
    }

    public void SetFlag(string key, bool enabled, string? value = null)
    {
        _flags[key] = new FeatureFlag { Key = key, is_enabled = enabled, Value = value };
    }

    // Implement other interface methods as needed...
}

Performance Testing

Benchmark Tests

using BenchmarkDotNet.Attributes;

[MemoryDiagnoser]
public class FeatureFlagPerformanceBenchmarks
{
    private IFeatureFlagService _flagService;

    [GlobalSetup]
    public void Setup()
    {
        var services = new ServiceCollection();
        services.AddFlaggy(new InMemoryFeatureFlagProvider());
        var provider = services.BuildServiceProvider();
        _flagService = provider.GetRequiredService<IFeatureFlagService>();

        // Pre-create flags
        _flagService.CreateFlagAsync(new FeatureFlag
        {
            Key = "benchmark-flag",
            is_enabled = true,
            Value = "test"
        }).Wait();
    }

    [Benchmark]
    public async Task<bool> CheckFlagEnabled()
    {
        return await _flagService.is_enabledAsync("benchmark-flag");
    }

    [Benchmark]
    public async Task<string?> GetFlagValue()
    {
        return await _flagService.GetValueAsync("benchmark-flag");
    }

    [Benchmark]
    public async Task<int?> GetFlagValueInt()
    {
        return await _flagService.GetValueAsync<int>("benchmark-flag", defaultValue: 0);
    }
}

Best Practices

Always Test Both Flag States

// Good - Tests both enabled and disabled states
[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task Feature_BothStates_WorkCorrectly(bool flagEnabled)
{
    await FlagService.UpsertFlagAsync("my-feature", flagEnabled);
    var service = new MyService(FlagService);

    var result = await service.DoWorkAsync();

    if (flagEnabled)
        Assert.True(result.UsedNewFeature);
    else
        Assert.False(result.UsedNewFeature);
}

Test Default Values

[Fact]
public async Task GetConfiguration_WhenFlagMissing_UsesDefaultValue()
{
    // Don't create the flag - test the default behavior
    var timeout = await FlagService.GetValueAsync<int>("request-timeout", defaultValue: 30);

    Assert.Equal(30, timeout.Value);
}

Use Descriptive Test Names

// Good - Clear what is being tested
[Fact]
public async Task CreateFlag_WhenKeyAlreadyExists_ReturnsFalse()

// Bad - Unclear test purpose
[Fact]
public async Task TestCreateFlag()

Next Steps