Skip to content

Selenide Tutorial for Beginners: Your First Test with Java & JUnit

You decided to start. You cloned the project. Now what?

If you’ve read The Right Time to Start QA Automation, you already know that the biggest blocker isn’t skill — it’s waiting for the “right moment” that never quite arrives. So this post skips the theory and gives you something concrete: a real project, real tests, and a place to make mistakes without consequences.

What You’ll Be Working With

The playground uses four things that work well together for beginners:

  • Java — the programming language. You don’t need to be a developer to write tests with it, but a basic understanding of classes and methods will help. More on that in a moment.
  • Selenide — a wrapper on top of Selenium WebDriver that removes a lot of the boilerplate. Less setup, shorter syntax, built-in waiting. I recommend going through its documentation before starting out – you can find it here.
  • JUnit 6 — the testing framework that tells Java which methods are tests, how to run them, and how to report results.
  • The Internet — a deliberately simple demo web app at https://the-internet.herokuapp.com, built specifically for automation practice. No real data, no real risk.

The project lives here: beginner-playground branch on GitHub

A Note on Java Knowledge

You don’t need to be a Java developer to follow this, but having a baseline will save you a lot of frustration early on. Before diving into the project, it’s worth spending a little time on the fundamentals — things like what a class is, what a method is, what static means, and how imports work.

Some free resources worth bookmarking:

  • Test Automation University — has a dedicated Java for testers path, free, well-structured
  • GeeksForGeeks – Java — good for looking up specific concepts when you get stuck
  • Baeldung — more advanced, but excellent as a reference once you start hitting real-world Java questions

You don’t need to complete a full course before starting. Even a few hours of reading on the basics will make the code in this post feel less like a foreign language.

Before You Write a Single Line

Make sure you have the following installed:

  • JDK 22 — the version this project uses. You can verify what’s on your machine with java -version in your terminal. If you need to install or update it, Adoptium is a reliable source.
  • IntelliJ IDEA — the Community edition is free and the most beginner-friendly IDE for Java. When you open the project, IntelliJ will recognise it as a Maven project and handle most of the setup for you.
  • Maven — the build tool that manages dependencies and runs the tests. If you want to run tests from the command line using mvn test, you’ll need Maven installed separately. You can follow the official installation guide — it’s straightforward. If you prefer to just run tests from inside IntelliJ for now, you can skip this step initially.
  • Chrome — the project is configured to run tests on Chrome, so make sure it’s installed.

Once you clone the repo and open it in IntelliJ, Maven will automatically download everything listed in pom.xml — including Selenide and JUnit. You don’t need to manually install them.

Understanding the Project Structure

Before running anything, take a minute to understand where things live:

There are 2 important directories under src: main and test. The tests you write live under test/java/ and later on we will see what we should add to the main/java/ path so that we can write cleaner code. But more on that in a separate post so we can keep things as simple as possible.

The pom.xml is the configuration file that tells Maven what libraries to download and how to build the project. You don’t need to edit it right now — just know it exists and that it’s what makes Selenide and JUnit available in your test code.

The .gitignore file tells GIT what file/folders based on provided patters to ignore and not track in the git repository. If you’re curious about it, you can read more on this topic on the official website here.

And there is also the README.md file that is intended to present the project, the setup and its purpose.

The Test Class: Walking Through It Together

Here’s the actual test class from the project:

public class HomepageTests {

private final SelenideElement homepageHeading = $("h1.heading");

@BeforeAll
public static void setup() {
Configuration.baseUrl = "https://the-internet.herokuapp.com/";
Configuration.browser = "chrome";
Configuration.downloadsFolder = "/build/downloads";
Configuration.timeout = 15000;
}

@Test
public void ensureTheHomepageOpensAndHasTheCorrectUrl() {
Selenide.open(Configuration.baseUrl);
webdriver().shouldHave(url(Configuration.baseUrl));
}

@Test
public void waitForHomepageToLoadAndTitleToBeDisplayed() {
Selenide.open(Configuration.baseUrl);
homepageHeading.shouldBe(visible, Duration.ofSeconds(20));
homepageHeading.shouldHave(text("Welcome to the-internet"), Duration.ofSeconds(10));
}

@AfterAll
public static void teardown() {
WebDriverRunner.driver().close();
}
}

Let’s go through this piece by piece.

@BeforeAll — Setup

@BeforeAll public static void setup() { Configuration.baseUrl = "https://the-internet.herokuapp.com/"; Configuration.browser = "chrome"; Configuration.timeout = 15000; }

@BeforeAll is a JUnit annotation. It tells JUnit to run this method once, before any tests in the class execute. This is where you configure Selenide’s behaviour for the entire test run.

Configuration.baseUrl sets the base URL so you don’t have to type the full address in every test. Configuration.browser tells Selenide which browser to use. Configuration.timeout sets how long (in milliseconds) Selenide will wait for elements before giving up — here I’ve set it for 15 seconds.

@AfterAll — Teardown

@AfterAll public static void teardown() { WebDriverRunner.driver().close(); }

@AfterAll runs once after all tests in the class have finished. This closes the browser. Without it, a browser window might stay open after the tests complete.

@Test — The Actual Tests

@Test public void ensureTheHomepageOpensAndHasTheCorrectUrl() { Selenide.open(Configuration.baseUrl); webdriver().shouldHave(url(Configuration.baseUrl)); }

@Test marks this method as a test case. JUnit picks it up and runs it.

Selenide.open(...) opens the browser and navigates to the URL. webdriver().shouldHave(url(...)) then checks that the browser actually landed on the expected URL. Simple, but it’s a valid test — confirming a page loads and you’re where you expect to be is a reasonable starting assertion.

@Test
public void waitForHomepageToLoadAndTitleToBeDisplayed() {
Selenide.open(Configuration.baseUrl);
homepageHeading.shouldBe(visible, Duration.ofSeconds(20));
homepageHeading.shouldHave(text("Welcome to the-internet"), Duration.ofSeconds(10));
}

This is where things get interesting. homepageHeading is defined at the top of the class:

private final SelenideElement homepageHeading = $("h1.heading");

The $() is Selenide’s selector method. The string inside uses CSS selector syntax — h1.heading targets an <h1> element with the class heading. Right-clicking any element in a browser and choosing Inspect will show you the HTML and help you build selectors.

shouldBe(visible, ...) waits for the element to become visible on the page. shouldHave(text(...), ...) checks that the element contains the expected text. Both include explicit timeout values, but Selenide also has a default timeout (set in @BeforeAll) that applies when you don’t specify one.

This is one of the things that makes Selenide easier to start with than raw Selenium — you express what you expect, and it handles the waiting automatically.

Running the Tests

From IntelliJ: Right-click on the test class or on an individual test method and choose Run. The browser will open, run through the test, and close. In the bottom panel, IntelliJ will show you a green checkmark (pass) or a red bar with a failure message.

From the command line (requires Maven installed): mvn test

To run a specific class: mvn test -Dtest=HomepageTests

The Surefire plugin in pom.xml is configured to pick up any class whose name ends in Tests — so as long as your new test classes follow that naming convention, they’ll be included automatically.

What to Try Next

The-internet has many pages worth practicing on. Start with the ones that map to scenarios you already test manually:

Checkboxeshttps://the-internet.herokuapp.com/checkboxes
Click a checkbox and verify its state. Good for learning how to interact with form elements beyond just text inputs.

Dropdownhttps://the-internet.herokuapp.com/dropdown
Select an option and verify the selection was applied. Dropdowns come up constantly in real applications.

Form Authentication (Login)https://the-internet.herokuapp.com/login
The login page is already referenced in the project. Try the happy path (valid credentials) but also the sad path (wrong password). What does the error message look like? How do you assert it?

Dynamic Loadinghttps://the-internet.herokuapp.com/dynamic_loading
An element loads only after a delay. This one is specifically useful for practicing waits and understanding why shouldBe(visible) matters.

For each of these, create a new test class in the test/java/ folder (remember: name it ending in Tests so Surefire picks it up) and write at least two tests — one that should pass, and one that verifies what happens when something goes wrong.

Deliberately Break Things

The most valuable thing you can do in a playground is experiment on purpose.

Try removing a .shouldBe(visible) assertion and see if the test still passes. What does that tell you about whether the assertion was actually doing anything? Or change the expected text on an element, or the expected URL.

Try using the wrong selector — something that doesn’t exist on the page. Read the error message carefully. Selenide’s failures are written to be human-readable: they tell you what element was expected, what condition it should meet, and what the actual state was.

Try running the dynamic loading test without any wait assertion — just open() and then immediately check for the element. Does it pass? Does it behave consistently if you run it multiple times?

These aren’t failures. That’s the process.

The Difference Between Selenide and Raw Selenium

If you’ve seen Selenium code before, here’s a quick comparison for the same action:

With Selenium WebDriver:

WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement element = wait.until(
ExpectedConditions.visibilityOfElementLocated(By.cssSelector("h1.heading"))
);
assertTrue(element.isDisplayed());

With Selenide:

$("h1.heading").shouldBe(visible);

Same result. Selenide handles the wait, the driver management, and the assertion in one line. As a beginner, this means less boilerplate to understand before you can focus on what actually matters: the test logic.

What You Should Walk Away With

After spending time in this playground, you should be able to:

  • Open a URL and interact with common page elements — inputs, buttons, dropdowns, checkboxes
  • Write assertions that verify something is visible, has certain text, or is in a certain state
  • Use @BeforeAll, @AfterAll, and @Test with a basic understanding of what each one does
  • Run tests from IntelliJ and read the output
  • Add a new test class and have it picked up automatically

That’s the starting point. You don’t need to understand CI pipelines or parallel execution or reporting tools to get value from this.

The goal right now is to make the connection between “I know how to test this manually” and “I can tell a computer to test it for me.

Everything else comes after that.

One Last Thing

The playground exists so that you have a safe place to be wrong. The-internet is not a real application. Nothing breaks. Nobody sees it. If your test fails, that’s not a problem — it’s information.

The habit that separates QAs who get comfortable with automation from those who stay stuck is simple: they run the tests, they read the failures, and they try again.

That’s it.

Go ahead, clone it and experiment on your own.