keycloak/test-framework
Ricardo Martin f41dab8173
Some checks are pending
Keycloak CI / Check conditional workflows and jobs (push) Waiting to run
Keycloak CI / Build (push) Blocked by required conditions
Keycloak CI / Base UT (push) Blocked by required conditions
Keycloak CI / Base IT (push) Blocked by required conditions
Keycloak CI / Adapter IT (push) Blocked by required conditions
Keycloak CI / Adapter IT Strict Cookies (push) Blocked by required conditions
Keycloak CI / Quarkus UT (push) Blocked by required conditions
Keycloak CI / Quarkus IT (push) Blocked by required conditions
Keycloak CI / Java Distribution IT/UT (push) Blocked by required conditions
Keycloak CI / Login Theme v1 tests (push) Blocked by required conditions
Keycloak CI / Volatile Sessions IT (push) Blocked by required conditions
Keycloak CI / External Infinispan IT (push) Blocked by required conditions
Keycloak CI / AuroraDB IT (push) Blocked by required conditions
Keycloak CI / AzureDB IT (push) Blocked by required conditions
Keycloak CI / Store IT (push) Blocked by required conditions
Keycloak CI / Store IT (additional) (push) Blocked by required conditions
Keycloak CI / Store Model Tests (push) Blocked by required conditions
Keycloak CI / Clustering IT (push) Blocked by required conditions
Keycloak CI / FIPS UT (push) Blocked by required conditions
Keycloak CI / FIPS IT (push) Blocked by required conditions
Keycloak CI / Forms IT (push) Blocked by required conditions
Keycloak CI / WebAuthn IT (push) Blocked by required conditions
Keycloak CI / SSSD (push) Blocked by required conditions
Keycloak CI / Migration Tests (push) Blocked by required conditions
Keycloak CI / Test Framework (push) Blocked by required conditions
Keycloak CI / Base IT (new) (push) Blocked by required conditions
Keycloak CI / Admin v2 (push) Blocked by required conditions
Keycloak CI / Cluster Compatibility Tests (push) Blocked by required conditions
Keycloak CI / Status Check - Keycloak CI (push) Blocked by required conditions
CodeQL / Check conditional workflows and jobs (push) Waiting to run
CodeQL / CodeQL Java (push) Blocked by required conditions
CodeQL / CodeQL JavaScript (push) Blocked by required conditions
CodeQL / CodeQL TypeScript (push) Blocked by required conditions
CodeQL / CodeQL GitHub Actions (push) Blocked by required conditions
CodeQL / Status Check - CodeQL (push) Blocked by required conditions
Keycloak Documentation / Check conditional workflows and jobs (push) Waiting to run
Keycloak Documentation / Build (push) Blocked by required conditions
Keycloak Documentation / External links check (push) Blocked by required conditions
Keycloak Documentation / Status Check - Keycloak Documentation (push) Blocked by required conditions
Keycloak Guides / Check conditional workflows and jobs (push) Waiting to run
Keycloak Guides / Build (push) Blocked by required conditions
Keycloak Guides / Status Check - Keycloak Guides (push) Blocked by required conditions
Keycloak JavaScript CI / Check conditional workflows and jobs (push) Waiting to run
Keycloak JavaScript CI / Build Keycloak (push) Blocked by required conditions
Keycloak JavaScript CI / Admin Client (push) Blocked by required conditions
Keycloak JavaScript CI / UI Shared (push) Blocked by required conditions
Keycloak JavaScript CI / Account UI (push) Blocked by required conditions
Keycloak JavaScript CI / Admin UI (push) Blocked by required conditions
Keycloak JavaScript CI / Account UI E2E (push) Blocked by required conditions
Keycloak JavaScript CI / Admin UI E2E (push) Blocked by required conditions
Keycloak JavaScript CI / Keycloak Admin Client (push) Blocked by required conditions
Keycloak JavaScript CI / Status Check - Keycloak JavaScript CI (push) Blocked by required conditions
Keycloak Operator CI / Check conditional workflows and jobs (push) Waiting to run
Keycloak Operator CI / Build distribution (push) Blocked by required conditions
Keycloak Operator CI / Test local apiserver (push) Blocked by required conditions
Keycloak Operator CI / Test remote (push) Blocked by required conditions
Keycloak Operator CI / Test OLM installation (push) Blocked by required conditions
Keycloak Operator CI / Status Check - Keycloak Operator CI (push) Blocked by required conditions
Use MIME decoder instead of the default one to replace deprecated Base64 class (#45325)
Closes #45226


(cherry picked from commit 1aa1621eaa)

Signed-off-by: rmartinc <rmartinc@redhat.com>
2026-01-11 17:29:34 +01:00
..
bom Support for EDB 17 (#42341) 2025-09-26 16:04:47 +02:00
clustering Fix Cluster Compatibility Tests 2026-01-07 15:32:00 +00:00
core Migrate parts of model package to new test framework (#45024) 2025-12-19 14:52:23 +01:00
db-edb Support EDB 18 (#44856) 2025-12-15 07:36:26 +01:00
db-mariadb Proposed import order (#43432) 2025-11-14 09:34:49 +01:00
db-mssql Added section on recommended isolation level to db guides 2025-12-05 14:48:31 +01:00
db-mysql Proposed import order (#43432) 2025-11-14 09:34:49 +01:00
db-oracle Proposed import order (#43432) 2025-11-14 09:34:49 +01:00
db-postgres Proposed import order (#43432) 2025-11-14 09:34:49 +01:00
db-tidb Proposed import order (#43432) 2025-11-14 09:34:49 +01:00
email-server Proposed import order (#43432) 2025-11-14 09:34:49 +01:00
examples Move AdminClientTest to the new testsuite (#44705) 2025-12-17 14:31:22 +00:00
junit5-config Support running test methods on the server side (#44937) 2025-12-17 13:15:42 +01:00
oauth Update testframework registry to explicitly declare dependencies in all suppliers (#44974) 2025-12-18 14:08:32 +01:00
remote Migrate parts of model package to new test framework (#45024) 2025-12-19 14:52:23 +01:00
remote-providers Use MIME decoder instead of the default one to replace deprecated Base64 class (#45325) 2026-01-11 17:29:34 +01:00
ui Update testframework registry to explicitly declare dependencies in all suppliers (#44974) 2025-12-18 14:08:32 +01:00
HOW_TO_RUN.md Fix Chrome and Firefox in new test framework on GitHub Actions (#44804) 2025-12-10 12:22:47 -03:00
LOGGING.md [Keycloak Test Framework] Infinispan cache + ClusterlessTestSuite configuration (#42172) 2025-09-17 07:13:11 +00:00
pom.xml Support for EDB 17 (#42341) 2025-09-26 16:04:47 +02:00
README.md Separate HOW_TO_RUN.md file for the new testsuite (#43860) 2025-11-03 15:41:01 +01:00

Introduction

The Keycloak JUnit 5 test framework makes it easy to write tests for Keycloak and extensions. Behind the scenes the framework handles the lifecycle of Keycloak, the database, and any injected resources such as realms and clients.

Tests simply declare what they want, including specific configuration, and the framework takes care of the rest.

Writing tests

An example is better than a lot of words, so here is a very basic test:

@KeycloakIntegrationTest
public class BasicTest {

    @InjectRealm
    ManagedRealm realm;

    @Test
    public void test() {
        Assertions.assertEquals("default", realm.getName());
        Assertions.assertEquals(0, realm.admin().users().list().size());
    }

}

Resource lifecycle

Managed resources can have the following life-cycles:

  • Global - Shared across multiple test classes
  • Class - Shared across multiple test methods within the same test class
  • Method - Only used for a single test method

The framework handles the lifecycle accordingly to how it is configured in the annotation, or the default lifecycle for a given resource.

For example the default lifecycle for a realm is Class, but it can be changed through the annotation:

@InjectRealm(lifecycle = LifeCycle.METHOD)
ManagedRealm realm;

@Test
public void test() {
    realm.admin().users().create(...);
}

@Test
public void test2() {
    Assertions.assertEquals(0, realm.admin().users().list().size());
}

When the lifecycle is set to Method the realm is automatically destroyed and re-created for each test method, as seen in the above example where one test method adds a user to the realm, but the user is not present in the next test.

The general recommendation is to use the Class lifecycle for realms, clients, and users. Making sure that individual test methods leave the resource in a way that can be re-used. Realms for example with global lifecycle can be harder to maintain as individual test classes can break other tests, but at the same time using global resources can be useful as it will be more performant.

Configuring resources

Resources are configured by declaring the required configuration through a Java class. This Java class can be an inner-class if it's only used for a single test class, or can be a proper class when multiple tests share the same configuration.

For example to create a realm with a specific configuration:

@InjectRealm(config = MyRealmConfig.class)
ManagedRealm realm;

static class MyRealmConfig implements RealmConfig {

    @Override
    public RealmRepresentation getRepresentation() {
        return builder()
                .name("myrealm")
                .groups("group-a", "group-b")
                .build();
    }
}

The framework will automatically re-create global resources if they don't match the required configuration. For example:

@KeycloakIntegrationTest
public class Test1 {

    @InjectRealm(lifecycle = LifeCycle.GLOBAL, config = MyRealmConfig.class)
    ManagedRealm realm;

}

@KeycloakIntegrationTest
public class Test2 {

    @InjectRealm(lifecycle = LifeCycle.GLOBAL, config = MyOtherRealm.class)
    ManagedRealm realm;

}

In this example the realm from Test1 would be destroyed and a new realm created for Test2 since different configuration is requested.

Multiple instances

By default, a resource does not have a reference, and child-resources are created within parent the resource without a reference. For example in the following example userA will be created within realmA:

@InjectRealm
ManagedRealm realmA;

@InjectUser
ManagedUser userA;

If you need for instance multiple realms within a test you need to set a reference on it, and use this reference for child resources:

@InjectRealm
ManagedRealm realmA;

@InjectUser
ManagedUser userA;

@InjectRealm(ref = "realmB")
ManagedRealm realmB;

@InjectUser(realmRef = "realmB")
ManagedUser userB;

As with resources without a reference if a resource is re-used in another test class compatibility will be checked. For example:

@KeycloakIntegrationTest
public class Test1 {
    @InjectRealm(lifecycle = LifeCycle.GLOBAL, ref = "realmA")
    ManagedRealm realmA;

    @InjectRealm(lifecycle = LifeCycle.GLOBAL, ref="realmB", config = MyRealmConfig.class)
    ManagedRealm realmB;
}

@KeycloakIntegrationTest
public class Test2 {
    @InjectRealm(lifecycle = LifeCycle.GLOBAL, ref = "realmA")
    ManagedRealm realmA;
    
    @InjectRealm(lifecycle = LifeCycle.GLOBAL, ref="realmB", config = MyOtherRealm.class)
    ManagedRealm realmB;
}

In the above example realmA will be reused both for Test1 and Test2, while realmB will be re-created between the two test classes since the required configuration differs.

Using the Keycloak admin client

The Keycloak admin client can be injected directly, which is automatically connected to the test server:

@InjectAdminClient
org.keycloak.admin.client.Keycloak keycloak;

@Test
public void testAdminClient() {
    keycloak.realms().findAll();
}

It is also available directly for a managed resource:

@InjectRealm
ManagedRealm realm;

@Test
public void testRealmAdmin() {
    realm.admin().users().list();
}

Using Selenium

Frequently when testing Keycloak it is required to interact with login pages, required actions, etc. through the browser. This can be done in two ways, where the most convenient way is to inject a Java Page representation:

@InjectPage
LoginPage loginPage;

@Test
public void testLogin() {
    // Do something to open the login page
    loginPage.fillLogin(..);
    loginPage.submit();
}

An alternative approach is to inject the WebDriver directly:

@InjectWebDriver
WebDriver webDriver;

@Test
public void test() {
    webDriver.switchTo().newWindow(WindowType.TAB);
}

OAuth Client

A convenient way to test OAuth flows are with the OAuth Client. This provides convenient methods to perform different OAuth flows, and it even automatically creates its own client within the realm. For example:

@InjectOAuthClient
OAuthClient oAuthClient;

@Test
public void testClientCredentials() throws Exception {
    TokenResponse tokenResponse = oAuthClient.clientCredentialGrant();
    Assertions.assertTrue(tokenResponse.indicatesSuccess());
    Assertions.assertNotNull(tokenResponse.toSuccessResponse().getTokens().getAccessToken());
}

Test Suites

A @Suite can supply configuration to be used when running tests from the suite. For example:

@Suite
@SelectClasses(MyTest.class)
public class MyTestSuite {

    @BeforeSuite
    public static void beforeSuite() {
        SuiteSupport.startSuite()
                .registerServerConfig(MyTestSuiteServerConfig.class)
                .includedSuppliers("server", "remote");
    }

    @AfterSuite
    public static void afterSuite() {
        SuiteSupport.stopSuite();
    }
}

The above example adds some additional Keycloak server configuration, as well as limiting what server suppliers can be used for the suite.

Running tests

For more information on how to run tests, see the How to run tests guide.