Skip to main content
Back to Blog
Integration
2026-05-15

Selenide JUnit 5 Spring Boot — Integration Guide 2026

Master Selenide with JUnit 5 and Spring Boot. WebApplicationFactory, LocalServerPort, fixtures, parallel execution, and CI/CD patterns.

Selenide JUnit 5 Spring Boot Integration Guide

Spring Boot is the dominant Java web framework, and most Spring Boot teams need browser-level integration tests at some point — to verify a multi-step form flow, validate that a server-rendered page renders correctly, or test JavaScript-driven features end to end. Pairing Spring Boot's testing infrastructure with Selenide gives you fast, expressive browser tests that share fixtures with your unit tests and reuse the application's own data access layer. The integration involves a few moving parts: @SpringBootTest with webEnvironment = RANDOM_PORT, Selenide configured to hit the local port, and a shared lifecycle that boots the app once per test class.

This guide is a comprehensive walkthrough of integrating Selenide with JUnit 5 and Spring Boot in 2026. We cover @SpringBootTest setup, @LocalServerPort injection, Selenide Configuration patterns, transactional test data, JUnit 5 parallel execution, Testcontainers integration for real databases, and CI/CD patterns. Every code sample is working Java with Selenide 7+, JUnit 5, Spring Boot 3, and JDK 21.


Key Takeaways

  • @SpringBootTest(webEnvironment = RANDOM_PORT) boots the app on a random port
  • @LocalServerPort injects the port into the test class
  • Selenide.open() can take a relative URL when Configuration.baseUrl is set
  • JUnit 5 SelenideExtension handles browser lifecycle cleanly
  • Testcontainers integration gives real database integration on top
  • Parallel execution requires per-thread driver isolation (built into Selenide)

Project Setup

Maven dependencies:

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>com.codeborne</groupId>
    <artifactId>selenide</artifactId>
    <version>7.5.0</version>
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>com.codeborne</groupId>
    <artifactId>selenide-junit5</artifactId>
    <version>7.5.0</version>
    <scope>test</scope>
  </dependency>
</dependencies>

Basic Test Structure

import com.codeborne.selenide.Configuration;
import com.codeborne.selenide.junit5.BrowserStrategyExtension;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;

import static com.codeborne.selenide.Selenide.*;
import static com.codeborne.selenide.Condition.*;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ExtendWith(BrowserStrategyExtension.class)
class LoginIntegrationTest {

    @LocalServerPort
    int port;

    @BeforeAll
    static void setupSelenide() {
        Configuration.browser = "chrome";
        Configuration.headless = true;
        Configuration.browserSize = "1920x1080";
    }

    @Test
    void userCanLogin() {
        open("http://localhost:" + port + "/login");
        $("#email").setValue("alice@example.com");
        $("#password").setValue("secret123");
        $("button[type=submit]").click();
        $("#dashboard").shouldBe(visible);
    }
}

Spring boots the app on a random port; Selenide opens the browser and runs the test.


Using Configuration.baseUrl

Setting baseUrl means you can use relative URLs:

@BeforeEach
void setBaseUrl() {
    Configuration.baseUrl = "http://localhost:" + port;
}

@Test
void usesRelativeUrls() {
    open("/login"); // resolves to http://localhost:8082/login
    $("#email").setValue("alice@example.com");
}

Database Setup with @Sql

import org.springframework.test.context.jdbc.Sql;

@Test
@Sql(scripts = "/test-data/users.sql")
void loginWithSeededUser() {
    open("/login");
    $("#email").setValue("seeded@example.com");
    $("#password").setValue("password");
    $("button[type=submit]").click();
    $("#dashboard").shouldBe(visible);
}

/test-data/users.sql:

INSERT INTO users (email, password_hash) VALUES
  ('seeded@example.com', '$2a$10$...');

@Transactional Behavior

By default, @SpringBootTest doesn't roll back changes. For test isolation, use @Transactional carefully — browser tests use real HTTP requests so transactions don't span the request boundary. Instead, manage test data explicitly:

@Autowired
private UserRepository userRepository;

@BeforeEach
void cleanUsers() {
    userRepository.deleteAll();
    userRepository.save(new User("alice@example.com", "hash"));
}

Testcontainers PostgreSQL Integration

For real-database tests, combine Selenide with Testcontainers:

import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers
@ExtendWith(BrowserStrategyExtension.class)
class FullStackTest {

    @Container
    static final PostgreSQLContainer<?> POSTGRES =
        new PostgreSQLContainer<>("postgres:16-alpine");

    @DynamicPropertySource
    static void props(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", POSTGRES::getJdbcUrl);
        registry.add("spring.datasource.username", POSTGRES::getUsername);
        registry.add("spring.datasource.password", POSTGRES::getPassword);
    }

    @LocalServerPort int port;

    @BeforeAll
    static void setup() {
        Configuration.browser = "chrome";
        Configuration.headless = true;
    }

    @Test
    void worksEndToEnd() {
        open("http://localhost:" + port + "/users");
        $$(".user").shouldHave(sizeGreaterThan(0));
    }
}

See our Postgres Java Spring Boot guide for deeper integration patterns.


SelenideExtension vs BrowserStrategyExtension

Selenide ships several JUnit 5 extensions:

ExtensionPurpose
BrowserStrategyExtensionManages browser lifecycle per class
SoftAssertsExtensionEnables soft assertions
TextReportExtensionGenerates plain text reports
ScreenShooterExtensionScreenshots on failure (also automatic)

Apply via:

@ExtendWith({BrowserStrategyExtension.class, SoftAssertsExtension.class})

Parallel Execution

junit-platform.properties:

junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.mode.classes.default=concurrent
junit.jupiter.execution.parallel.config.strategy=fixed
junit.jupiter.execution.parallel.config.fixed.parallelism=4

Selenide handles per-thread WebDriver isolation. Each test thread gets its own browser session.

For Selenium Grid, see our parallel testing guide.


Mock External Services

Use Spring Boot's testing infrastructure for mocking:

import org.springframework.boot.test.mock.mockito.MockBean;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class PaymentTest {

    @MockBean
    private StripeClient stripeClient;

    @Test
    void paymentSucceeds() {
        when(stripeClient.charge(any())).thenReturn(new ChargeResult("succeeded"));

        open("/checkout");
        // ... fill form, submit
        $(".success").shouldBe(visible);
    }
}

WireMock for External APIs

If your app calls external HTTP APIs, mock them with WireMock:

import com.github.tomakehurst.wiremock.WireMockServer;
import org.junit.jupiter.api.AfterAll;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ExternalApiTest {

    static WireMockServer wireMock;

    @BeforeAll
    static void startMock() {
        wireMock = new WireMockServer(8089);
        wireMock.start();
    }

    @AfterAll
    static void stopMock() {
        wireMock.stop();
    }

    @Test
    void worksWithMockedApi() {
        wireMock.stubFor(get(urlEqualTo("/api/external"))
            .willReturn(okJson("[{\"id\":1,\"name\":\"Test\"}]")));

        open("/data");
        $(".item").shouldHave(text("Test"));
    }
}

Configure the app under test to point at http://localhost:8089 via test properties.


CI/CD Configuration

name: integration-tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: 21
      - uses: browser-actions/setup-chrome@v1
      - run: ./mvnw verify
      - uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: screenshots
          path: build/reports/tests/

Allure Integration

For rich reports:

import com.codeborne.selenide.logevents.SelenideLogger;
import io.qameta.allure.selenide.AllureSelenide;

@BeforeAll
static void setupAllure() {
    SelenideLogger.addListener("allure", new AllureSelenide()
        .screenshots(true)
        .savePageSource(true)
        .includeSelenideSteps(true)
    );
}

See our AllureSelenide reference for details.


Common Pitfalls

Pitfall 1: Port collisions. Don't hardcode ports; use @LocalServerPort.

Pitfall 2: Test data leaks. Browser tests use HTTP requests, not Spring transactions. Clean data explicitly.

Pitfall 3: Browser state between tests. Use closeWebDriver() or rely on per-class lifecycle.

Pitfall 4: Headless quirks. Test in headed mode locally if headless behavior surprises you.

Pitfall 5: Spring context startup time. Each @SpringBootTest class restarts Spring. Use @ContextConfiguration to share contexts across classes.


Conclusion

Selenide + JUnit 5 + Spring Boot is the modern Java integration testing stack for 2026. Combine with Testcontainers for real databases, AllureSelenide for rich reports, and Selenium Grid for parallel cross-browser coverage. The result is fast, reliable, end-to-end tests that share fixtures with your unit tests and validate the whole application stack.

For complementary patterns, see our Postgres Java Spring Boot guide and Selenide vs Selenium WebDriver 2026.

Browse the QA skills directory for related Java testing patterns.

Selenide JUnit 5 Spring Boot — Integration Guide 2026 | QASkills.sh