EvSoftware.Infrastructure.Tests.Keycloak
0.1.0
dotnet add package EvSoftware.Infrastructure.Tests.Keycloak --version 0.1.0
NuGet\Install-Package EvSoftware.Infrastructure.Tests.Keycloak -Version 0.1.0
<PackageReference Include="EvSoftware.Infrastructure.Tests.Keycloak" Version="0.1.0" />
<PackageVersion Include="EvSoftware.Infrastructure.Tests.Keycloak" Version="0.1.0" />
<PackageReference Include="EvSoftware.Infrastructure.Tests.Keycloak" />
paket add EvSoftware.Infrastructure.Tests.Keycloak --version 0.1.0
#r "nuget: EvSoftware.Infrastructure.Tests.Keycloak, 0.1.0"
#:package EvSoftware.Infrastructure.Tests.Keycloak@0.1.0
#addin nuget:?package=EvSoftware.Infrastructure.Tests.Keycloak&version=0.1.0
#tool nuget:?package=EvSoftware.Infrastructure.Tests.Keycloak&version=0.1.0
EvSoftware.Infrastructure.Tests.Keycloak
This library provides a helper class (KeycloakHelper) designed to simplify integration testing of .NET applications secured by Keycloak, particularly when using Testcontainers to manage the Keycloak instance.
It allows you to programmatically interact with the Keycloak Admin REST API within your test setup (e.g., xUnit fixtures) to:
- Create realms
- Create OIDC or SAML clients
- Create users with credentials
- Obtain access tokens for test users via password grant
This avoids the need for static realm import files and makes test setup more dynamic and maintainable directly within your C# test code.
Prerequisites
- .NET 8.0 (or compatible)
- Docker environment accessible by Testcontainers
Installation
dotnet add package EvSoftware.Infrastructure.Tests.Keycloak
Usage
Typically, you would use this helper within an xUnit test fixture (IAsyncLifetime) that manages a Keycloak Testcontainer.
1. Setup Keycloak Testcontainer Fixture:
using DotNet.Testcontainers.Builders;
using DotNet.Testcontainers.Containers;
using EvSoftware.Infrastructure.Tests.Keycloak;
using System.Threading.Tasks;
using Xunit;
// Example Fixture (like KeycloakFixture.cs in integration tests)
public class MyKeycloakTestFixture : IAsyncLifetime
{
private readonly IContainer _keycloakContainer;
public KeycloakHelper KeycloakHelper { get; private set; } = null!;
public string KeycloakBaseUrl { get; private set; } = null!;
private const string KeycloakImage = "quay.io/keycloak/keycloak:22.0.1"; // Or desired version
private const string AdminUser = "admin";
private const string AdminPassword = "admin";
public MyKeycloakTestFixture()
{
_keycloakContainer = new ContainerBuilder()
.WithImage(KeycloakImage)
.WithPortBinding(8080, true) // Map to random available host port
.WithEnvironment("KEYCLOAK_ADMIN", AdminUser)
.WithEnvironment("KEYCLOAK_ADMIN_PASSWORD", AdminPassword)
.WithCommand("start-dev") // Start in dev mode (HTTP)
.WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(8080))
.Build();
}
public async Task InitializeAsync()
{
await _keycloakContainer.StartAsync();
KeycloakBaseUrl = $"http://{_keycloakContainer.Hostname}:{_keycloakContainer.GetMappedPublicPort(8080)}";
KeycloakHelper = new KeycloakHelper(KeycloakBaseUrl, AdminUser, AdminPassword);
}
public async Task DisposeAsync()
{
KeycloakHelper?.Dispose();
if (_keycloakContainer != null)
{
await _keycloakContainer.StopAsync();
await _keycloakContainer.DisposeAsync();
}
}
}
// Define a collection fixture
[CollectionDefinition("MyKeycloakTests")]
public class KeycloakCollection : ICollectionFixture<MyKeycloakTestFixture> { }
2. Use Helper in Tests:
Inject the fixture into your test class and use the KeycloakHelper instance.
using EvSoftware.Infrastructure.Tests.Keycloak.Representations;
using System.Threading.Tasks;
using Xunit;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net;
[Collection("MyKeycloakTests")]
public class MyApiIntegrationTests
{
private readonly MyKeycloakTestFixture _fixture;
private readonly KeycloakHelper _keycloakHelper;
private readonly HttpClient _apiClient; // Your API client
public MyApiIntegrationTests(MyKeycloakTestFixture fixture)
{
_fixture = fixture;
_keycloakHelper = fixture.KeycloakHelper;
_apiClient = new HttpClient { BaseAddress = new Uri("http://localhost:5000") }; // Example API URL
}
[Fact]
public async Task ProtectedEndpoint_ShouldReturnOk_WithValidToken()
{
// Arrange: Set up Keycloak using the helper
var realmName = "test-realm";
var clientId = "test-api-client";
var username = "test-user";
var password = "password123";
await _keycloakHelper.CreateRealmAsync(new RealmRepresentation { Realm = realmName, Enabled = true });
await _keycloakHelper.CreateClientAsync(realmName, new ClientRepresentation
{
ClientId = clientId,
PublicClient = true, // Assuming public client for simplicity
DirectAccessGrantsEnabled = true // Needed for password grant
});
await _keycloakHelper.CreateUserAsync(realmName, new UserRepresentation
{
Username = username,
Credentials = new[] { new CredentialRepresentation { Type = "password", Value = password } }
});
// Get token
string accessToken = await _keycloakHelper.GetAccessTokenAsync(realmName, clientId, username, password);
// Act: Call protected API endpoint
_apiClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var response = await _apiClient.GetAsync("/api/protected");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}
Included DTOs
The library includes basic Data Transfer Object (DTO) classes under the EvSoftware.Infrastructure.Tests.Keycloak.Representations namespace to represent Keycloak entities:
RealmRepresentationClientRepresentationUserRepresentationCredentialRepresentation
These can be used when calling the KeycloakHelper methods.
Contributing
Contributions are welcome! Please feel free to open an issue or submit a pull request.
License
This project is licensed under the MIT License - see the LICENSE file for details (assuming you add one).
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- System.Net.Http.Json (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.0 | 165 | 4/5/2025 |