Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Seed the backend before Selenium opens the React page. In Spring integration tests, use repeatable SQL fixtures (usually with @Sql) or a setup API/database step. Then let Selenium perform only the user workflow and assertions. This separation is faster, easier to isolate, and less brittle than clicking through the UI to create every prerequisite record.

Choose the fixture mechanism for the test lifecycle

Spring Boot has two related but different data-loading mechanisms:

  • Application startup initialization runs while the application starts. Its ordering depends on how the schema is created and which Boot version you use.
  • Spring TestContext SQL runs for selected test classes or methods. @Sql can execute scripts before or after a test and can be customized with @SqlConfig.

Use startup initialization when every application launch needs the same baseline. Use test-managed fixtures when a particular test needs a known record set. Check the reference documentation for your exact Spring Boot and Spring Framework versions; annotation merging and transaction behavior are version-sensitive.

Option 1: Seed an integration test with @Sql

Create test-only schema and data files

Put scripts under src/test/resources. Keep the fixture small and name records so the test intent is obvious.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
-- src/test/resources/sql/customer-test-data.sql
INSERT INTO customers (id, email, display_name, status)
VALUES ('selenium-customer-001', 'selenium@example.test', 'Selenium Customer', 'ACTIVE');

-- src/test/resources/sql/customer-cleanup.sql
DELETE FROM customers WHERE id = 'selenium-customer-001';

Adapt table names, columns, keys, and quoting to your schema. If migrations create the schema, let migrations run first and load only rows that satisfy current constraints.

Attach the fixture to a Spring test

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.jdbc.Sql;
import static org.springframework.test.context.jdbc.Sql.ExecutionPhase.AFTER_TEST_METHOD;

@SpringBootTest
@Sql(scripts = "/sql/customer-test-data.sql")
@Sql(
    scripts = "/sql/customer-cleanup.sql",
    executionPhase = AFTER_TEST_METHOD
)
class CustomerApiTest {
    @Test
    void returnsTheSeededCustomer() {
        // Call the application API and assert the response.
    }
}

A method-level declaration can target one scenario instead of the whole class. If class and method declarations must combine or replace one another, configure the merge mode explicitly and verify the behavior against the Spring Framework version in your build.

Handle transactions deliberately

By default, a Spring test may run inside a test-managed transaction. That is useful for rollback isolation, but it can surprise a browser or a separately running API client: uncommitted rows are not visible outside the transaction. If the seeded record must be visible to Selenium, either commit the setup in a separate transaction or run the setup outside the transaction used by the assertion. Configure this with your test transaction strategy and, where needed, @SqlConfig; do not assume a script is committed merely because it completed.

Option 2: Prepare data through an API or database step

For an end-to-end React workflow, create the account, order, or other record before launching the browser. A supported test-only API is often preferable because it exercises the same validation and authorization boundaries without making Selenium navigate setup screens. A direct database fixture is appropriate when no setup API exists or when the test specifically concerns persistence.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. Generate a unique identifier (for example, a UUID) for this test run.
  2. Create the record through a test API or a fixture transaction, and retain its identifier.
  3. Start a fresh WebDriver session and authenticate the way the application requires.
  4. Open the React route that displays the record.
  5. Perform only the user action under test and assert the visible result.
  6. Delete or expire the record, then quit the driver even when the assertion fails.
// Pseudocode; replace endpoints and selectors with your application values
const id = crypto.randomUUID();
await api.post('/test-support/customers', {
  id, email: `${id}@example.test`, status: 'ACTIVE'
});

const driver = await new Builder().forBrowser('chrome').build();
try {
  await driver.get(`${baseUrl}/customers/${id}`);
  await driver.findElement(By.css('[data-testid="customer-menu"]')).click();
  await driver.findElement(By.css('[data-testid="customer-status"]'))
    .getText();
} finally {
  await api.delete(`/test-support/customers/${id}`);
  await driver.quit();
}

The exact association between a seeded backend row and a React screen depends on your routes, authentication, API contract, and client state management. The title does not establish one universal React fixture API, so keep that integration explicit in your project.

Option 3: Use Testcontainers when database fidelity matters

A disposable real database is useful when production-specific SQL, constraints, indexes, or transaction behavior must be represented. Testcontainers can start a temporary database for the test suite; a Spring Boot example uses a PostgreSQL container together with @SpringBootTest and @Sql. This requires a working container runtime and adds startup time. Choose it when that fidelity is worth the operational cost; otherwise a simpler test database can make feedback faster.

@SpringBootTest
@Testcontainers
class CustomerBrowserDataTest {
    @Container
    static PostgreSQLContainer<?> postgres =
        new PostgreSQLContainer<>("postgres:16");

    // Supply the container JDBC URL, username, and password to Spring
    // using the integration mechanism supported by your Boot version.

    @Test
    @Sql("/sql/customer-test-data.sql")
    void browserScenarioUsesPostgres() {
        // Start the app against the disposable container, then test it.
    }
}

The container image, service-connection support, and configuration differ across Spring Boot generations. Treat a PostgreSQL example as a pattern, not a universal dependency recipe.

Drive the React UI with Selenium after setup

Wait for application state, not arbitrary sleeps

React renders asynchronously. Prefer an explicit wait for a route, element, or meaningful state rather than a fixed delay.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(15));
driver.get(baseUrl + "/customers/" + customerId);

WebElement status = wait.until(
    ExpectedConditions.visibilityOfElementLocated(
        By.cssSelector("[data-testid='customer-status']")));
assertEquals("Active", status.getText());

Stable data-testid attributes are generally less fragile than CSS classes generated by a component library. Wait for the UI state that proves the API response has been applied.

Keep browser assertions narrow

Use Selenium for navigation, clicks, keyboard input, and what a user can observe. Test validation rules, repository behavior, and most error branches at lower layers. Selenium describes functional browser tests as comparatively expensive; moving non-visual coverage into unit or integration tests shortens the suite and reduces maintenance.

Isolation rules for repeatable runs

  • Unique data: include a run or test identifier in emails, external keys, and order numbers.
  • No shared mutable records: two tests should not update the same customer or depend on another test’s ordering.
  • Fresh sessions: use a distinct WebDriver instance per test when your runner and resources permit; clear cookies and local storage when reuse is unavoidable.
  • Cleanup: remove rows in an after-test script or API call, and also arrange periodic cleanup for abandoned runs.
  • Parallel safety: make selectors, accounts, ports, and database schemas independent so parallel workers cannot observe each other’s state.

Selenium’s guidance on avoiding shared state is especially relevant when tests are retried or run concurrently. A clean database alone does not isolate browser cookies, local storage, or server-side sessions.

Which approach should you use?

Approach Best fit Strength Trade-off
@Sql fixture Spring integration tests with known rows Versioned, repeatable, close to the test Must manage schema compatibility and transaction visibility
Setup API End-to-end tests needing realistic application creation Uses application validation without browser setup Requires a safe test-support endpoint and authentication
Direct database setup No suitable API or large fixture needed Fast and deterministic Can bypass application rules and couple tests to schema
Testcontainers Database-specific behavior and constraints Production-like engine in a disposable environment Container runtime and slower startup required
Browser-created data Only when the creation journey itself is under test Tests the complete user journey Slowest and most brittle setup

Common failures and fixes

“Table does not exist” or an insert fails on startup

Your script ran before migrations or used a different schema. Run migrations first, place the script in the test lifecycle, and confirm the datasource initialization ordering for your Boot version.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Selenium cannot see the seeded row

The setup transaction may still be uncommitted, the browser may use another database, or the React app may request a different tenant. Commit setup data, log the effective JDBC URL, and verify the request identity and route identifier.

Duplicate-key errors on retries

A previous run left data behind or parallel workers reused a fixed key. Generate unique IDs and make cleanup idempotent, for example by deleting by a run-specific prefix.

The page is blank or assertions race the API

Wait for a meaningful element or loading state, inspect browser and server logs, and verify that the seeded record satisfies every field the React API expects. Replace sleeps with explicit waits.

Tests pass alone but fail in the suite

Look for shared rows, reused cookies, static ports, and test-order assumptions. Give each test its own records and driver, and reset external state between cases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Testcontainers cannot start

Check that the container runtime is running, the image is available, and the test process can reach the mapped port. If database fidelity is not required, use a non-container test database to reduce environmental dependencies.

Performance and reliability checklist

  • Seed only the rows needed for the scenario.
  • Prefer one setup API call or one SQL script over dozens of UI interactions.
  • Reuse a container for a suite only when your reset strategy guarantees isolation; otherwise start a disposable instance per scope.
  • Capture server logs, browser console output, screenshots, and the test data identifier on failure.
  • Set bounded page and explicit-wait timeouts; an unbounded wait hides infrastructure failures.
  • Run a small browser smoke set on every change and keep broad data/validation coverage below the browser layer.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is to capture a page image for a test artifact or review rather than drive the React workflow, ScreenshotNeo returns a screenshot or PDF from one request. Its cleaner capture accepts cookie/consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing result. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo documentation for all options, including full-page lazy-image loading, CSS-selector element capture, custom JavaScript, waits, headers, cookies, device and viewport settings, PDF output, caching, webhooks, and bulk jobs. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.

FAQ

Should fixture SQL live beside production migrations?

Keep test-only rows and scripts under test resources so they cannot be deployed accidentally. Share migration definitions only when the test genuinely validates them.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Can Selenium create the records through the React form?

Yes, when creation is the behavior being tested. For unrelated prerequisites, an API or database setup is more reliable and keeps the browser scenario focused.

Is an in-memory database equivalent to production?

Not necessarily. SQL dialects, constraints, indexing, and transaction behavior can differ. Use a disposable production-engine container when those differences could change the result.

Frequently Asked Questions

Should fixture SQL live beside production migrations?

Keep test-only rows and scripts under test resources so they cannot be deployed accidentally. Share migration definitions only when the test genuinely validates them.

Can Selenium create the records through the React form?

Yes, when creation is the behavior being tested. For unrelated prerequisites, an API or database setup is more reliable and keeps the browser scenario focused.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Is an in-memory database equivalent to production?

Not necessarily. SQL dialects, constraints, indexing, and transaction behavior can differ. Use a disposable production-engine container when those differences could change the result.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.