Skip to main content

JUnit Integration

SHAFT Engine supports JUnit (Jupiter) as a first-class test runner alongside TestNG. All SHAFT APIs, reporting, and properties configuration work identically regardless of which test framework you choose.


Maven Dependency​

Add the JUnit Jupiter engine to your pom.xml:

pom.xml
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
info

SHAFT Engine's pom.xml may already include JUnit transitively. Check your dependency tree with mvn dependency:tree before adding it manually.


Runtime Registration​

SHAFT Engine registers its JUnit support through the Java ServiceLoader mechanism:

  • com.shaft.listeners.JunitListener is registered as a JUnit Platform LauncherSessionListener.
  • com.shaft.listeners.JunitExtension is registered as a Jupiter extension.
  • junit.jupiter.extensions.autodetection.enabled=true is supplied through junit-platform.properties.

Generated JUnit projects also create or update src/test/resources/junit-platform.properties without replacing existing settings. If you maintain that file yourself, keep this line in it:

src/test/resources/junit-platform.properties
junit.jupiter.extensions.autodetection.enabled=true

You do not need to add @ExtendWith to ordinary SHAFT JUnit tests.

The listener and extension together provide the same SHAFT lifecycle behavior that TestNG users get: engine setup and teardown, Allure attachments, execution summary rows, telemetry, Jira/Xray handoff, retries, fail-fast skips, linked issue skips, setup/teardown failure reporting, and soft-verification failures that fail the JUnit run.

JUnit retries use the same SHAFT flag as TestNG retries: retryMaximumNumberOfAttempts. Configure it in custom.properties, pass it as -DretryMaximumNumberOfAttempts=2, or update it at runtime with SHAFT.Properties.flags.set().retryMaximumNumberOfAttempts(2).

The value is the number of additional attempts after the first failure. For example, retryMaximumNumberOfAttempts=1 runs the original attempt plus one retry. In JUnit, SHAFT schedules the retry after the failed attempt's @AfterEach lifecycle has completed and executes the retry through JUnit again, so the retry receives a fresh @BeforeEach setup. Put browser creation in @BeforeEach and driver cleanup in @AfterEach when retries need independent browser sessions.

note

TestNG remains supported. TestNG XML suite shaping and TestNG retry analyzer binding are still TestNG runner features. For JUnit cross-browser coverage, use JUnit parameterized tests and SHAFT per-thread properties instead of relying on TestNG XML cloning.


Test Class Structure​

WebTestJUnit.java
import com.shaft.driver.SHAFT;
import org.junit.jupiter.api.*;
import org.openqa.selenium.By;

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class WebTestJUnit {
private static SHAFT.GUI.WebDriver driver;

@BeforeEach
void beforeEach() {
driver = new SHAFT.GUI.WebDriver();
}

@Test
@Order(1)
void navigateAndAssertTitle() {
driver.browser().navigateToURL("https://duckduckgo.com/")
.and().assertThat().browser().title().contains("DuckDuckGo");
}

@Test
@Order(2)
void searchAndVerifyResults() {
driver.browser().navigateToURL("https://duckduckgo.com/");
driver.element().type(By.id("searchbox_input"), "SHAFT Engine");
driver.element().click(By.id("search_button_homepage"));
driver.assertThat().browser().url().contains("q=SHAFT");
}

@AfterEach
void afterEach() {
driver.quit();
}
}

JUnit vs TestNG Comparison​

FeatureJUnitTestNG
Setup annotation@BeforeEach@BeforeMethod
Teardown annotation@AfterEach@AfterMethod
Class-level setup@BeforeAll@BeforeClass
Test ordering@TestMethodOrder + @Orderpriority on @Test
Test annotation@Test (Jupiter)@Test (TestNG)
Parameterized tests@ParameterizedTest@DataProvider

Class-Level Driver (Shared Instance)​

Use @BeforeAll / @AfterAll with a static driver for tests that share a single browser session:

SharedDriverJUnit.java
import com.shaft.driver.SHAFT;
import org.junit.jupiter.api.*;
import org.openqa.selenium.By;

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class SharedDriverJUnit {
private static SHAFT.GUI.WebDriver driver;

@BeforeAll
static void setupAll() {
driver = new SHAFT.GUI.WebDriver();
driver.browser().navigateToURL("https://example.com/login");
driver.element()
.type(By.id("email"), "test@example.com")
.type(By.id("password"), "secret")
.click(By.id("loginBtn"));
}

@Test
@Order(1)
void verifyDashboardLoaded() {
driver.assertThat().browser().url().contains("/dashboard");
}

@Test
@Order(2)
void verifyUserNameDisplayed() {
driver.assertThat(By.id("userName")).text().contains("Test User");
}

@AfterAll
static void teardownAll() {
driver.quit();
}
}

tip

Use @BeforeEach / @AfterEach (fresh driver per test) for independent test isolation. Use @BeforeAll / @AfterAll (shared driver) for ordered workflow tests where each step builds on the previous one.

note

SHAFT's JUnit listener and extension are automatically registered via the Java ServiceLoader mechanism.