Cheat Sheet
Setup & Installation
- Java 17+: Required for Neodymium 5.x+
- Maven: Used for build and dependency management
- Docker: Use
maven:3-openjdk-17ormarkhobson/maven-chrome:jdk-17for quick setup - FFmpeg: (Optional) For mp4 video recording of test runs
Defining and Running Tests
JUnit5
@Browser
public class SomeTest
{
@NeodymiumTest
public void someTestMethod()
{
// ...
}
}
JUnit4
@RunWith(NeodymiumRunner.class)
@Browser
public class SomeTest
{
@Test
public void someTestMethod()
{
// ...
}
}
Run Tests
- In IDE as JUnit test
mvn clean test(all tests)mvn clean test -Dtest=TestClass(specific class)mvn clean test -Dtest=TestClass#testMethod(specific method)
Generate Reports
mvn allure:reportto generate the reportmvn allure:serveto generate and show the reportmvn clean test allure:reportto run all tests and generate the report
Recommended pom.xml
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<allure.version>2.32.0</allure.version>
<aspectj.version>1.9.22</aspectj.version>
<neodymium.version>5.3.0</neodymium.version>
<surefire.version>3.5.2</surefire.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire.version}</version>
<configuration>
<testFailureIgnore>true</testFailureIgnore>
<argLine>
-javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
</argLine>
<systemPropertyVariables>
<allure.results.directory>${project.build.directory}/allure-results</allure.results.directory>
<selenide.reports>${project.build.directory}/selenide-results</selenide.reports>
</systemPropertyVariables>
</configuration>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-maven</artifactId>
<version>${allure.version}</version>
<configuration>
<reportVersion>2.32.0</reportVersion>
<resultsDirectory>${project.build.directory}/allure-results</resultsDirectory>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.xceptance</groupId>
<artifactId>neodymium-library</artifactId>
<version>${neodymium.version}</version>
</dependency>
</dependencies>
Properties & Configuration
The most important configuration files are listed below:
neodymium.propertiesbrowser.propertiescredentials.propertiesgif-recording.propertiesvideo-recording.properties
Properties files must reside in the ./config/ directory at the project root. dev-*.properties files can be used to override or add values.
Loading order (highest priority first):
- system properties
- temporary config file
config/dev-neodymium.properties- system environment variables
config/credentials.propertiesconfig/neodymium.properties
In cases where a property is found in several files, the value from the file with the highest priority is used. Further read: Neodymium properties
Key Properties
neodymium.url = ${neodymium.url.protocol}://${neodymium.url.host} # Base URL for tests
neodymium.url.protocol = https
neodymium.url.host = posters.xceptance.io
neodymium.locale = en_US # Default locale
neodymium.localization.file = config/localization.yaml # Localization file
Project Templates
- neodymium-template: Starter project for new users (GitHub)
- neodymium-example: Standard Java-based tests (GitHub)
- neodymium-cucumber-example: BDD with Cucumber (GitHub)
- neodymium-showcase: Advanced features and recipes (GitHub)
Browser & WebDriver Features
Neodymium WebDriver functions
Neodymium.getDriver()- get current WebDriverNeodymium.getEventFiringWebdriver()- get current WebDriver as EventFiringWebDriverNeodymium.getRemoteWebDriver()- get RemoteWebDriver if wrapped in current WebDriverNeodymium.getBrowserProfileName()- get used browser profile nameNeodymium.getBrowserName()- get used browser nameNeodymium.getLocalProxy()- get embedded local proxyNeodymium.getWebDriverStateContainer()- get state and the objects belonging to the current execution of the browser. Contains the current WebDriver, the embedded local BrowserUpProxy if used and a counter that state how often the current execution setup was used
Key Properties
neodymium.webDriver.keepBrowserOpen = false # Keep browser open after test
neodymium.webDriver.keepBrowserOpenOnFailure = false # Keep browser open on failure
neodymium.webDriver.reuseDriver = false # Reuse browser sessions
neodymium.webDriver.maxReuse = -1 # Max reuse count (-1 = unlimited)
neodymium.webDriver.browserFilter = <browserId> # Filter browsers for test run
neodymium.webDriver.startNewBrowserForSetUp = true # New browser for setup
neodymium.webDriver.startNewBrowserForCleanUp = true # New browser for cleanup
neodymium.webDriver.chrome.pathToDriverServer = <path> # Path to ChromeDriver
neodymium.webDriver.firefox.pathToDriverServer = <path> # Path to GeckoDriver
neodymium.webDriver.edge.pathToDriverServer = <path> # Path to EdgeDriver
neodymium.webDriver.safari.driverArguments = "" # Safari driver args
Browser Profiles
Define in
config/browser.properties:browserprofile.Chrome_large.name = Chrome Large browserprofile.Chrome_large.browser = chrome browserprofile.Chrome_large.browserResolution = 1920x1080 browserprofile.Chrome_large.headless = false browserprofile.Chrome_large.arguments = -ignore-certificate-errors; --remote-allow-origins=*Reference in test on class or method:
@Browser("Chrome_large")
Multi-Browser & Filtering
Annotate with multiple
@Browserfor multi-browser runsFilter with property:
neodymium.webDriver.browserFilter = Chrome_large,Firefox_large
Device Emulation
- Use
chromeEmulationProfilein browser profile for mobile/tablet emulation
Selenium Grid & Cloud
Configure remote environments in
credentials.propertiesand reference in browser profile:browserprofile.testEnvironment.saucelabs.url = https://ondemand.saucelabs.com:443/wd/hub browserprofile.Chrome_SauceLabs.testEnvironment = saucelabs
Keep Browser Open
Set in properties or use annotation:
neodymium.webDriver.keepBrowserOpen = true@KeepBrowserOpen(onlyOnFailure = false)
WebDriver Reuse
- Enable reuse and set max reuse:
neodymium.webDriver.reuseDriver = true neodymium.webDriver.maxReuse = 2
Separate Setup/Cleanup Browsers
@StartNewBrowserForSetUp
@BeforeEach
public void setUp()
{ /* ... */ }
@StartNewBrowserForCleanUp
@AfterEach
public void cleanUp()
{ /* ... */ }
Test Data & Context
Key Properties
neodymium.testData.email.domain = varmail.de # Default email domain
neodymium.testData.email.local.prefix = test # Email local part prefix
neodymium.testData.email.randomCharsAmount = 12 # Email random chars
neodymium.testData.password.uppercaseCharAmount = 2 # Password uppercase chars
neodymium.testData.password.lowercaseCharAmount = 5 # Password lowercase chars
neodymium.testData.password.digitAmount = 2 # Password digits
neodymium.testData.password.specialCharAmount = 2 # Password special chars
neodymium.testData.password.specialChars = +-#$%&.;,_ # Allowed special chars
- Access test data:
Neodymium.getData().get("key") - Use
@DataItemfor auto-injection:@DataItem("$.field") String field; - Thread-local context:
Neodymium.getDriver(),Neodymium.getBrowserProfileName(), etc.
Test Data
Methods can have multiple @DataSet() annotations
@DataItem- annotate POJO representing the test data to automatically inject the data into it@DataSet(id="<dataSetId>")- limit method to data set with defined id@DataSet(<index>)- limit method to dataset with defined index@RandomDataSets(4)- use 4 random data sets@SuppressDataSets- donāt use any data set@DataFile("<path/to/file>")- define path to test data if default should not be used
Supported file formats ordered by priority:
- CSV
- JSON
- XML
- properties
ā ļø Attention
Only JSON is currently supported for complex test data with nested objects! Simple key/value pairs can be used in all formats.
Test data access:
Neodymium.dataValue("<dataKey>")Neodymium.getData().asString("<key>")-as...()function for all primitive types@DataItemPOJO - usual object accessNeodymium.getData().get(<testDataClass.class>)- parses data manually into POJO instead of using@DataItem
Package test data to share common data among tests within the same package and its sub-packages in a file named
package_testdata.
Localization
localization.yamlstored in./config/localization.yamlcontaining key value pairs of translations.- locale defined in
neodymium.propertiesasneodymium.locale=<locale>or changed during tests usingNeodymium.configuration().setProperty("neodymium.locale", <locale>).
Get localized texts during tests:
Neodymium.localizedText("<key>")- get localized text for current localeNeodymium.localizedText("<key>", "<locale>")- get localized text for given locale
How to use different localization inside data driven tests:
Data sets define locale
[ { "locale": "en_US", ... }, { "locale": "de_DE", ... } ]Use base test class to handle locale for all tests
public abstract class AbstractTest { @BeforeEach public void setUpLocaleAndSite() { // get the locale from the test data String locale = Neodymium.getData().getOrDefault("locale", ""); // and set it if it was provided if (!locale.isEmpty()) { Neodymium.configuration().setProperty("neodymium.locale", locale); } } }
Configuration & Properties
Key Properties
neodymium.properties = <main config file>
config/dev-neodymium.properties = <dev override>
config/credentials.properties = <cloud credentials>
neodymium.testNameFilter = <regex> # Filter tests by name/class/method/dataset
neodymium.context.device.breakpoint.small = 576 # Responsive breakpoints
neodymium.context.device.breakpoint.medium = 768
neodymium.context.device.breakpoint.large = 992
neodymium.context.device.breakpoint.xlarge = 1200
Main config:
config/neodymium.propertiesOverride for dev:
config/dev-neodymium.propertiesTemporary:
ConfigFactory.setProperty("neodymium.temporaryConfigFile", "file:...properties")Filter tests by name/class/method/dataset:
neodymium.testNameFilter = .*Checkout.*
Utility Classes
- SelenideAddons: Regex matching, safe execution, slider handling, open HTML snippets, optional waits
- AllureAddons: Add steps, links, attachments, JSON to Allure reports
- JavaScriptUtils: Wait for page readiness, inject popup blocker
- TestData: POJO instantiation, type conversion, existence checks
- WebDriverUtils: Cucumber/WebDriver helpers
Reporting
Key Properties
neodymium.report.showSelenideErrorDetails = false # Selenide error details in report
neodymium.report.enableTestDataInReport = true # Attach test data to report
neodymium.report.environment.enableCustomData = true # Custom env data in report
neodymium.report.environment.enableBrowserData = true # Browser data in report
neodymium.report.enableStepLinks = true # Add page links to report
neodymium.allureAddons.screenshots.perstep.always = false # Screenshot per step
neodymium.allureAddons.reports.path = /build/reports/tests/ # Allure report path
neodymium.seleniumLogLevel = SEVERE # Selenium log level
Allure Reports
- Allure Reporting
- Add to
pom.xmland use:mvn allure:reportto generate the report as static HTML filesmvn allure:serveto generate and open the report in the browser using a temporary webservermvn clean test allure:reportto run all tests and generate the report
- Add steps/attachments in code with
AllureAddons
- Add to
@Step("description with {parameter}")add description with parameter to function@Step("click on a product by product name '{productName}'") public ProductDetailPage clickProductByName(String productName) { // ... }add custom info to report using
AllureAddons.addToReport("<message>");@NeodymiumTest public void myTest1() { AllureAddons.addToReport("first part"); // do something that would occur after the step "INFO: {first part}" in the report }add custom step with lambda without function and @Step annotation
@NeodymiumTest public void myTest2() { AllureAddons.step("first part", () -> { // do something that would occur within the step "first part" in the report }); }Links: Automatically added to steps where the URL changes.
Test Data: Automatically attached if
@DataItemorNeodymium.getData()is used.JSON Compare: Using
JsonAssert.assertEquals(expected, actual, mode)adds comparison with difference highlighting.Environment: Automatically contains Neodymium version and browsers. Extend with
neodymium.report.environment.custom.customData=customValue(requiresneodymium.report.environment.enableCustomData=true).
Logging
Use SLF4J/Log4j, configure in
pom.xmlandlog4j.propertiesControl Selenium log level:
neodymium.seleniumLogLevel = INFO
XcMailr Plugin
- Add dependency, configure
xcmailr.properties, useXcMailrApifor mailbox/email management
Applitools Plugin
- Add dependency, configure
applitools.properties, useApplitoolsApifor visual assertions
Jenkins & CI
- Use Allure plugin, Docker images, and provided pipeline scripts for CI/CD
Advanced Features
Key Properties
# pop up blocker
neodymium.popup.<name> = <CSS selector> # Popup blocker selectors
neodymium.popupInterval = 1000 # Popup check interval (ms)
# accessibility / lighthouse
neodymium.lighthouse.binaryPath = lighthouse # Lighthouse binary path
neodymium.lighthouse.assert.thresholdScore.performance = 0.5
neodymium.lighthouse.assert.thresholdScore.accessibility = 0.5
neodymium.lighthouse.assert.thresholdScore.bestPractices = 0.5
neodymium.lighthouse.assert.thresholdScore.seo = 0.5
# advanced screenshots
neodymium.screenshots.enableAdvancedScreenshots = false # Advanced screenshots
neodymium.screenshots.enableOnSuccess = false # Screenshots on success
neodymium.screenshots.fullpagecapture.enable = false # Full-page screenshots
neodymium.screenshots.fullpagecapture.highlightViewport = false # Highlight viewport
neodymium.screenshots.fullpagecapture.highlightColor = #FF0000 # Highlight color
neodymium.screenshots.blurFullPageScreenshot = false # Blur outside viewport
neodymium.screenshots.highlightLastElement = false # Highlight last element
neodymium.screenshots.element.highlightColor = #FF00FF # Last element color
neodymium.screenshots.highlightLineThickness = 4 # Highlight line thickness
# proxy / local proxy
neodymium.proxy = false # Use HTTP proxy
neodymium.proxy.host = <none> # Proxy host
neodymium.proxy.port = <none> # Proxy port
neodymium.localproxy = false # Use local MITM proxy
neodymium.localproxy.certificate = false # Use self-signed cert
neodymium.localproxy.certificate.generate = true # Generate cert
neodymium.localproxy.certificate.archiveFile = ./config/Certificates.p12
neodymium.localproxy.certificate.archivetype = PKCS12
neodymium.localproxy.certificate.name = <none>
neodymium.localproxy.certificate.password = <none>
# work in progress for tests
neodymium.workInProgress = false # Only run WIP tests
- Popup Blocker: Define selectors in properties, auto-injected by JavaScriptUtils
- Accessibility/Lighthouse: Set thresholds in properties, run audits
- Advanced Screenshot: Enable full-page, highlight, blur, and element screenshots in properties
- Proxy/Local Proxy: Configure HTTP/local MITM proxy in properties
- Responsive Helpers: Use Neodymium context/device helpers for adaptive tests
- Work In Progress: Run only WIP tests/classes with
@WorkInProgressorneodymium.workInProgress=true - Randomization: Use
Neodymium.getRandom()for reproducible random data
For more details and code examples, see the full documentation or the referenced example projects.
Parallel Execution
Parallel execution is supported via Maven Surefire Plugin. It works on class level (each class in its own JVM).
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<forkCount>4</forkCount>
<reuseForks>true</reuseForks>
</configuration>
</plugin>
See Parallel Execution.
Debug Highlighting
Highlights elements during test execution for better visual debugging.
neodymium.debugUtils.highlight = true # Enable highlighting
neodymium.debugUtils.highlight.duration = 100 # Highlight duration in ms
Advanced Screenshots
Must be enabled in neodymium.properties: neodymium.screenshots.enableAdvancedScreenshots=true.
Customization properties:
neodymium.screenshots.fullpagecapture.enable = true
neodymium.screenshots.highlightViewport = false
neodymium.screenshots.highlightLastElement = true
neodymium.screenshots.highlightColor = #0000FF
neodymium.screenshots.enableOnSuccess = false
Recording
Configuration in config/gif-recording.properties and config/video-recording.properties. Must have
<gif|video>.enableFilming = true.
| Property | Format | Description |
|---|---|---|
| enableFilming | bool | Enable recording |
| filmAutomatically | bool | Start recording automatically |
| deleteRecordingsAfterAddingToAllureReport | bool | Deletes recording file after adding to report |
| appendAllRecordingsToAllureReport | bool | If false, only failures are added |
| imageQuality | double | Image quality percentage (0.0 - 1.0) |
Manual recording:
FilmTestExecution.startVideoRecording(String<fileName>);
// do stuff
FilmTestExecution.finishVideoFilming(String<fileName>, boolean <testFailed>);
Accessibility
Requires
npm install -g lighthouse.Create report:
LighthouseUtils.createLightHouseReport("Homepage");.Assertions in properties:
neodymium.lighthouse.assert.thresholdScore.performance = 0.5 neodymium.lighthouse.assert.thresholdScore.accessibility = 0.5 neodymium.lighthouse.assert.thresholdScore.bestPractices = 0.5 neodymium.lighthouse.assert.thresholdScore.seo = 0.5 neodymium.lighthouse.assert.audits = audit1 audit2
PopUp Blocker
Will close non-deterministic pop-ups automatically if configured:
neodymium.popup.customPopUp=#myWindowCloseButton
PageObjects and Components
The Page Object Model (POM) wraps all elements and functionality of a web page into an object.
import com.codeborne.selenide.SelenideElement;
import io.qameta.allure.Step;
import static com.codeborne.selenide.Condition.visible;
import static com.codeborne.selenide.Selenide.$;
public class HomePage
{
private final SelenideElement homePageBanner = $("#intro-text-homepage-banner");
@Step("validate homepage")
public HomePage assertExpectedPage()
{
homePageBanner.shouldBe(visible);
return this;
}
@Step("open category {position}")
public ProductListingPage openCategoryAtPosition(int position)
{
$("#category-picture-" + (position - 1)).scrollTo().click();
return new ProductListingPage().assertExpectedPage();
}
}
Selenide Basics
For more details on Selenide please refer to the official documentation
Navigation
Selenide.open(<url>)- opens a URL inside the configured BrowserSelenide.open(Neodymium.configuration().url())- open the configured URL from the neodymium.propertiesSelenide.open(AuthenticationType.BASIC,new BasicAuthCredentials(Neodymium.configuration().basicAuthUsername(), Neodymium.configuration().basicAuthPassword())- open the configured URL with the configured credentials
Element Selection
$(<"CSS locator>")- select single element via CSS$$("<CSS locator>")- select collection of elements via CSS$x("<xPath>")- select single element via xPath$$x("<xPath>")- select collection of elements via xPath$$("<locator>").findBy(exactText("some text"))- find an element inside a list with a specific text
Interaction
$("<locator>").click()- clicks an element (scrolls the element into view upfront)$("<locator>").scrollTo()- scrolls to the element$("<locator>").hover()- hovers over an element$("<locator>").setValue(<value>)- sets the value attribute of an element$("<locator>").sendKeys(Keys.ENTER)- sends the enter key to the element
Validations
$("<locator>").shouldBe(visible)- validates that the element is visible$("<locator>").should(exist)- validates that the element exists$("<locator>").shouldNot(exist)- validates that the does not exist
Advanced Interactions
Sliders: Neodymium’s
SelenideAddonscan handle sliders.// the slider element that will be used for the test SelenideElement sliderMove = $("<locator of the moveable part of the slider>"); SelenideElement sliderValue = $("<locator of the value part of the slider>"); // The following method call will move the slider element 40px to the right and check for [aria-valuenow=8], // and will retry this movement 3 times with a 2000 seconds delay in between two tries. SelenideAddons.dragAndDropUntilCondition( sliderMove, // the element that will be moved sliderValue, // the element that will be checked for the attribute 40, // pixel to move (horizontal) 0, // pixel to move (vertical) 2000, // pause in ms between two tries 3, // max number of retries attribute("aria-valuenow","8") // the attribute that should be gained );Shadow DOM: Find elements inside a shadow dom.
$(Selectors.shadowCss("#target-element", "#shadowhost-element")).click()
iFrames: Switch to an iframe (and back).
switchTo().frame($("<locator>"));- switch into iFrameswitchTo().defaultContent();- switch back