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

Use Selenium WebDriver to exercise the application in a real browser, TestNG to run the test and manage setup and teardown, and JDBC to prepare test data or confirm what the application persisted. Keep the browser flow and database assertion as distinct steps, give each test its own record, and close both browser and JDBC resources even when an assertion fails.

What each part does

  • Selenium WebDriver drives the browser: it navigates to the application, fills forms, clicks controls, and reads visible results. Selenium setup requires a language binding, a browser, and a compatible browser driver. See Selenium WebDriver setup guidance.
  • TestNG runs and organizes Java tests, provides assertions, and offers lifecycle annotations for setup and teardown. Selenium lists TestNG among Java test-runner options. See TestNG and Selenium test setup.
  • JDBC connects Java code to a database, executes queries or updates, and retrieves results. Oracle’s Java tutorial prefers DataSource for connection management, while using DriverManager in simpler examples. See Oracle’s JDBC tutorial.

A browser-only assertion answers whether the user saw the expected outcome. A JDBC assertion can additionally answer whether the expected state was stored. Use the database check when persistence is part of the requirement, rather than using direct database access as a substitute for exercising the user flow.

Set up the Java project

Choose compatible dependencies

Add the Selenium Java binding, TestNG, and the JDBC driver for your database to the test runtime. Install the browser under test and its matching WebDriver implementation. Dependency versions and browser-driver compatibility change, so check the current official project pages when setting up a new project.

The TestNG site lists version 7.9.0 and states that TestNG 7.6.0 and newer requires JDK 11 or higher. Treat those as release-page details to verify before adopting them; do not assume they are the latest available. The Oracle JDBC tutorial is written for JDK 8 and warns that some examples may not reflect newer releases.

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.

Keep environment details out of test code

Supply the application base URL, database connection configuration, and credentials through the project’s runtime configuration and secret-management approach, not hard-coded source constants. TestNG’s @Parameters can pass values declared in testng.xml into test or configuration methods, and @Optional can define a default. TestNG documents parameter passing, but does not prescribe a secret-storage system; use the one appropriate to your build and deployment environment. See TestNG parameters.

Design a repeatable database-backed test

  1. Prepare unique data. Create or identify a record for this test, preferably through an application API or a narrowly scoped JDBC operation. Use a unique identifier, such as a generated email address, so another test cannot overwrite the same row.
  2. Start the browser and perform the user flow. Create a WebDriver session, navigate to the application, and interact with it through Selenium as a user would.
  3. Check the visible result. Assert the relevant page or UI state with TestNG. This localizes failures in the browser flow.
  4. Check persistence when required. Query for the test-owned record by its unique key and assert the expected stored values. Keep this check separate from the UI assertion so a failure identifies which layer did not meet the expectation.
  5. Clean up and close resources. Remove only data owned by the test, then quit the browser and close JDBC resources, including when an assertion or query throws.

TestNG’s configuration annotations let you put setup and cleanup at method, class, or suite scope. Per-method setup is a useful starting point when tests need isolated state. Class- or suite-level setup may avoid repeated expensive initialization, but shared mutable browser or database state must be controlled. TestNG documents lifecycle methods such as @BeforeSuite, @BeforeClass, and @BeforeMethod, and corresponding after methods; choose scope according to what the tests can safely share. See TestNG annotations.

Example: submit a profile and verify the database

This Java example shows the core flow with TestNG, Selenium, and JDBC. It assumes the project provides a configured DataSource, a valid application URL, suitable page locators, and a compatible Chrome browser/driver. Replace those application-specific pieces and add the appropriate imports and assertion dependency for your project. The example is illustrative, not a tested implementation.

public class ProfilePersistenceTest {
    private final DataSource dataSource = TestConfig.dataSource();
    private final String appBaseUrl = TestConfig.appBaseUrl();

    @Test
    public void savedProfileAppearsInDatabase() throws SQLException {
        String email = "test-" + UUID.randomUUID() + "@example.invalid";
        WebDriver driver = new ChromeDriver();

        try {
            driver.get(appBaseUrl + "/signup");

            // Replace locators with the application's actual form controls.
            driver.findElement(By.name("email")).sendKeys(email);
            driver.findElement(By.cssSelector("button[type='submit']")).click();

            // Assert the application's visible success state here.

            try (Connection connection = dataSource.getConnection();
                 PreparedStatement statement = connection.prepareStatement(
                     "select email from users where email = ?")) {
                statement.setString(1, email);
                try (ResultSet results = statement.executeQuery()) {
                    Assert.assertTrue(results.next(), "Expected profile row was not found");
                    Assert.assertEquals(results.getString("email"), email);
                }
            }
        } finally {
            driver.quit();
            // Delete only this test's record using an application cleanup path or JDBC.
        }
    }
}

For a robust project test, ensure cleanup runs even if the browser setup or database assertion fails. A test framework teardown method can be clearer than an inline finally block once browser and data lifecycles are shared across multiple tests. The snippet leaves deletion as a project-specific operation because the correct cleanup path depends on the application and schema.

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.

Use JDBC safely for setup and assertions

Bind values instead of building SQL strings

Use a PreparedStatement for variable values and bind each value with the corresponding setter, such as setString. Do not concatenate user or test input into SQL. Oracle’s JDBC tutorial demonstrates placeholders and setters and notes that prepared statements can be reused with different values. See Oracle’s prepared statement guidance.

Close connections and results reliably

Use try-with-resources for Connection, PreparedStatement or other statement objects, and ResultSet when their lifetimes fit inside the block. Java closes them automatically when control leaves the block, including when an exception occurs. Avoid keeping a connection open for the whole browser interaction unless the test specifically requires a transaction spanning that work.

Keep queries narrow

Query by the unique identifier the test created and select only the fields needed for the assertion. This reduces ambiguity if the database contains other test data and makes it clearer whether the application stored the intended state.

Choose lifecycle scope and execution mode

Choice When it fits Trade-off
Method-level setup and teardown Tests need separate browser sessions or isolated data. May repeat initialization, but keeps test state easier to reason about.
Class- or suite-level setup Initialization is expensive and state can safely be shared. Shared mutable state creates coupling and can make failures order-dependent.
Serial execution Establishing a stable baseline or tests still share resources. Does not use TestNG’s parallel execution capabilities.
Parallel execution Each test has isolated data and its own appropriately scoped WebDriver session. Shared-record contention and database locking behavior depend on the schema, database, and test design.

TestNG supports thread pools and parallel test modes. Start serially, establish repeatable behavior, then add concurrency only after each test owns separate data and browser state. See TestNG documentation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshoot common failures

  • WebDriver cannot start the browser: verify that the browser is installed and that the WebDriver implementation is compatible with it; confirm the Java Selenium binding is on the test runtime classpath.
  • TestNG tests do not run: check that TestNG is a test dependency, that the test is included in the suite XML or build configuration, and that the test uses TestNG annotations. Confirm the JDK meets the selected TestNG version’s requirements.
  • Database connection fails: verify the JDBC driver for the chosen database is present, runtime connection settings are correct, and credentials are available to the test process. Avoid putting credentials in source code.
  • The database row is missing: confirm the browser submitted successfully and that the persistence operation completed before the query. Inspect the unique key used by both the form and query, and distinguish an unsuccessful UI flow from a failed write.
  • Tests pass alone but fail in a suite: look for reused identifiers, cleanup that removes another test’s data, or shared browser/database state. Give each test unique data and use the narrowest practical lifecycle scope.
  • Parallel runs are flaky: return to serial execution, then isolate records and WebDriver sessions before re-enabling concurrency. Database lock and contention remedies are specific to the database and schema.
  • SQL values break the query: replace string concatenation with placeholders and bind values through PreparedStatement setters.

Or skip the browser setup

If you need a screenshot of the application page as part of a development workflow, ScreenshotNeo offers a website screenshot API and MCP server for developers. One GET request returns a PNG, JPEG, WebP, or PDF; its screenshot capture is separate from running a Selenium/TestNG test and does not replace database assertions. The API accepts parameters used by other screenshot APIs, which can make switching straightforward.

Example cURL call (replace the target URL and use your API key):

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 API documentation for request options. Cookie and consent banners, newsletter popups, and chat widgets are removed before capture by default; each step can be turned off. Bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and responses include X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots.

Sign up for ScreenshotNeo’s free plan to get 1,000 screenshots a month with no card.

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

FAQ

Should I verify every database-backed UI test with JDBC?

No. Use a database assertion when persistence is part of the behavior under test; otherwise, a UI assertion may be sufficient and keeps the test focused on what the user can observe.

Can I use DriverManager instead of DataSource?

Yes. Oracle’s Java tutorial uses DriverManager in simpler examples, while identifying DataSource as the preferred connection approach.

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.