Test Automation

The Ultimate 2025 Guide: 5 Steps to Your MJFS Selenium Stack

Ready to build a robust test automation framework in 2025? Follow our 5-step guide to mastering the MJFS (Maven, JUnit 5, Faker, Selenium) stack.

D

David Miller

Senior QA Automation Engineer specializing in scalable, Java-based testing frameworks and CI/CD.

7 min read4 views

Introduction: Why Your Test Stack Needs an Upgrade

In the fast-paced world of web development, test automation is no longer a luxury—it's a necessity. But as applications become more complex and release cycles shrink, the tools we use must evolve. Relying on outdated or piecemeal frameworks leads to brittle tests, maintenance nightmares, and a slower time-to-market. For 2025 and beyond, you need a cohesive, powerful, and modern stack. Enter MJFS.

This guide will walk you through building a robust automation framework from the ground up using the MJFS Stack: Maven, JUnit 5, Faker, and Selenium. We'll cover everything from project setup to writing clean, dynamic tests. By the end of this article, you'll have a clear, 5-step roadmap to creating a professional-grade automation solution that is scalable, maintainable, and ready for the challenges of modern web applications.

What is the MJFS Selenium Stack?

MJFS isn't an official product, but a curated collection of best-in-class, open-source tools that work seamlessly together for Java-based test automation. Each component plays a critical role in creating a comprehensive and efficient framework.

M for Maven: Your Project's Backbone

Apache Maven is a powerful project management and build automation tool. For our stack, it handles two key jobs: dependency management and running the build lifecycle. Instead of manually downloading JAR files for Selenium, JUnit, and other libraries, you simply declare them in a central pom.xml file. Maven downloads them for you, resolves conflicts, and ensures consistency. It also provides a standard way to compile code, run tests, and package your project.

J for JUnit 5: Modern Java Testing

JUnit is the de-facto standard for unit testing in Java. JUnit 5 is a complete rewrite, designed with modern programming paradigms in mind. It offers a modular architecture (Jupiter, Platform, Vintage), a rich set of annotations (@DisplayName, @Nested, @ParameterizedTest), and a powerful extension model. This makes writing expressive, organized, and powerful tests simpler than ever.

F for Faker: Realistic Test Data on Demand

Hardcoding test data (like "test@test.com") is a recipe for flaky tests. What if that user already exists? What if the data format changes? The Java Faker library solves this problem by generating realistic, random data on the fly. Need a name, address, email, or even a line from a Shakespeare play? Faker can generate it, making your tests more robust and dynamic.

S for Selenium: The Gold Standard in Browser Automation

Selenium WebDriver remains the undisputed leader for browser automation. Selenium 4 brought significant improvements like the W3C WebDriver Protocol becoming the default, relative locators, and better CDP (Chrome DevTools Protocol) integration. It provides the core functionality for our stack: opening browsers, interacting with web elements, and verifying application behavior just as a real user would.

Step 1: Setting Up Your Maven Project

First, ensure you have Java (JDK 11 or newer) and Apache Maven installed on your system. Once that's done, you can create a new Maven project using your favorite IDE or the command line. The most crucial part is the pom.xml file, which defines your project's dependencies.

Here’s a minimal pom.xml to get you started with the MJFS stack:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.yourcompany</groupId>
    <artifactId>mjfs-selenium-framework</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- Selenium WebDriver -->
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>4.18.1</version>
        </dependency>

        <!-- JUnit 5 -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.10.2</version>
            <scope>test</scope>
        </dependency>

        <!-- Java Faker -->
        <dependency>
            <groupId>com.github.javafaker</groupId>
            <artifactId>javafaker</artifactId>
            <version>1.0.2</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- To run tests from command line -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.2.5</version>
            </plugin>
        </plugins>
    </build>
</project>

After saving this file, your IDE or a `mvn clean install` command will download all the necessary libraries.

Step 2: Structuring Your Test Framework

A good structure is key to a maintainable framework. The Page Object Model (POM) is a widely accepted design pattern that helps achieve this.

The Page Object Model (POM) Advantage

The core idea of POM is to create a separate Java class for each page (or major component) of your web application. This class will contain:

  • Locators for the elements on that page (e.g., `By usernameField = By.id("username");`).
  • Methods that perform actions on those elements (e.g., `public void enterUsername(String username)`).
This approach decouples your test logic from the UI implementation. If a button's ID changes, you only need to update it in one place—the page object class—instead of hunting through dozens of test scripts.

Base Test and Driver Management

To avoid repetitive code, create a `BaseTest` class. This class will handle the setup and teardown of the WebDriver instance for each test. Using JUnit 5's `@BeforeEach` and `@AfterEach` annotations makes this clean and simple.

// In src/test/java/com/yourcompany/base/BaseTest.java
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import java.time.Duration;

public class BaseTest {
    protected WebDriver driver;

    @BeforeEach
    public void setUp() {
        // In a real framework, use a WebDriver manager library
        driver = new ChromeDriver();
        driver.manage().window().maximize();
        driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
    }

    @AfterEach
    public void tearDown() {
        if (driver != null) {
            driver.quit();
        }
    }
}

Your test classes will then extend this `BaseTest` to inherit the browser management logic.

Step 3: Writing Your First Test with JUnit 5 & Selenium

Let's tie it all together. Imagine we're testing a login page. First, create the `LoginPage` page object.

// In src/test/java/com/yourcompany/pages/LoginPage.java
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;

public class LoginPage {
    private WebDriver driver;

    // Locators
    private By usernameInput = By.id("username");
    private By passwordInput = By.id("password");
    private By loginButton = By.tagName("button");
    private By successMessage = By.id("flash");

    public LoginPage(WebDriver driver) {
        this.driver = driver;
    }

    // Actions
    public void enterUsername(String username) {
        driver.findElement(usernameInput).sendKeys(username);
    }

    public void enterPassword(String password) {
        driver.findElement(passwordInput).sendKeys(password);
    }

    public void clickLoginButton() {
        driver.findElement(loginButton).click();
    }

    public String getSuccessMessageText() {
        return driver.findElement(successMessage).getText();
    }
}

Now, write the test class that uses this page object. Notice how it extends `BaseTest`.

// In src/test/java/com/yourcompany/tests/LoginTest.java
import com.yourcompany.base.BaseTest;
import com.yourcompany.pages.LoginPage;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class LoginTest extends BaseTest {

    @Test
    @DisplayName("User should be able to log in with valid credentials")
    public void testSuccessfulLogin() {
        driver.get("https://the-internet.herokuapp.com/login");
        LoginPage loginPage = new LoginPage(driver);

        loginPage.enterUsername("tomsmith");
        loginPage.enterPassword("SuperSecretPassword!");
        loginPage.clickLoginButton();

        String successMessage = loginPage.getSuccessMessageText();
        Assertions.assertTrue(
            successMessage.contains("You logged into a secure area!"),
            "Success message was not found."
        );
    }
}

Step 4: Integrating Faker for Dynamic Test Data

Our previous test used hardcoded credentials. Let's improve it by using Faker for a registration test. First, add Faker to your `BaseTest` or a dedicated data utility class.

// In BaseTest or a new utility class
import com.github.javafaker.Faker;

protected Faker faker = new Faker();

Now, you can write a test that uses dynamically generated data.

// In a new RegistrationTest.java
@Test
@DisplayName("User should be able to register a new account")
public void testNewUserRegistration() {
    // driver.get("http://example.com/register");
    // RegistrationPage registrationPage = new RegistrationPage(driver);

    String firstName = faker.name().firstName();
    String lastName = faker.name().lastName();
    String email = faker.internet().emailAddress();
    String password = faker.internet().password(8, 16, true, true, true);

    System.out.println("Registering user: " + firstName + " " + lastName);
    // registrationPage.enterFirstName(firstName);
    // registrationPage.enterLastName(lastName);
    // registrationPage.enterEmail(email);
    // registrationPage.enterPassword(password);
    // registrationPage.clickSubmit();

    // Add assertions to verify successful registration
    // Assertions.assertTrue(registrationPage.isRegistrationSuccessful());
}

This makes each test run unique, helping you catch edge cases related to data handling that you might otherwise miss.

Step 5: Running Tests and Viewing Reports

Thanks to Maven and the Surefire plugin we added to our `pom.xml`, running your entire test suite is incredibly simple. Just open a terminal in your project's root directory and run:

mvn test

Maven will compile your code, discover all classes with JUnit 5 `@Test` annotations, execute them, and generate reports. By default, Surefire creates simple XML and TXT reports in the `target/surefire-reports` directory. For more visually appealing and detailed reports, you can easily integrate third-party listeners like Allure or ExtentReports.

Automation Stack Comparison: MJFS vs. The World

How does the MJFS stack compare to other popular automation solutions? Here's a quick breakdown:

Automation Framework Stack Comparison
StackPrimary LanguageTest RunnerKey AdvantageBest For
MJFS (Our Stack)JavaJUnit 5Robust, scalable, and strongly-typed. Excellent for large, enterprise-level projects.Teams with Java skills looking for a stable, long-term automation solution.
Python/Pytest/SeleniumPythonPytestSimple, clean syntax and a rich ecosystem of libraries. Fast to get started.Teams that prioritize development speed and have Python expertise. Great for web scraping and data-driven tests.
JavaScript/CypressJavaScriptBuilt-inAll-in-one tool with excellent debugging features ('time travel'). Runs in the same loop as the app.Frontend developers and teams wanting to write tests quickly for modern JS frameworks (React, Vue, Angular).
JavaScript/PlaywrightJavaScript/TSBuilt-in / JestDeveloped by Microsoft. Offers outstanding cross-browser support and auto-waits.Teams needing reliable automation across Chromium, Firefox, and WebKit with a single API.

Conclusion: Your Path to Automation Mastery

By following these five steps, you've laid the foundation for a powerful, modern, and maintainable test automation framework. The MJFS stack—Maven, JUnit 5, Faker, and Selenium—provides a professional-grade solution that balances structure with flexibility. You now have a system for managing dependencies, writing clean and expressive tests, generating dynamic data, and running your suite with a single command.

This is your starting point. From here, you can explore integrations with CI/CD pipelines like Jenkins or GitHub Actions, implement visual regression testing, or expand your reporting capabilities. The robust foundation you've built will support these future enhancements, ensuring your automation efforts deliver continuous value.