Java SDK for Visual Regression Tracker
Track screenshots from Java tests in a Visual Regression Tracker server. Requires Java 25 or later. Built and tested against VRT server 6.0; image URLs need server 5.0.10 or later.
The SDK is published on JitPack. The version is a git tag; see releases.
repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
testImplementation 'com.github.Visual-Regression-Tracker:sdk-java:6.0.0'
}<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependency>
<groupId>com.github.Visual-Regression-Tracker</groupId>
<artifactId>sdk-java</artifactId>
<version>6.0.0</version>
<scope>test</scope>
</dependency>The SDK logs through SLF4J 2. Add a provider such as logback-classic to see its output.
- Java 25 or later is required.
- SLF4J 1.7 bindings are not picked up; use an SLF4J 2 provider (logback 1.3+,
log4j-slf4j2-impl). - The API is unchanged. A call that passes a literal
nullor a bare Mockitoany()as the image is now ambiguous between theString,byte[]andPathoverloads oftrack; useany(String.class).
| Option | Environment variable | Required | Description |
|---|---|---|---|
apiUrl |
VRT_APIURL |
yes | URL of the VRT API, for example http://localhost:4200; trailing slashes are ignored |
apiKey |
VRT_APIKEY |
yes | API key of a VRT user |
project |
VRT_PROJECT |
yes | Project name or ID |
branchName |
VRT_BRANCHNAME |
no | Branch of the build. If not set, the build gets the project's main branch |
sourceBranch |
VRT_SOURCEBRANCH |
no | Branch whose baselines a test without a baseline on branchName is compared against. If not set, the project's main branch |
ciBuildId |
VRT_CIBUILDID |
no | ID of the CI build. Starting a build with an ID that already exists continues that build |
enableSoftAssert |
VRT_ENABLESOFTASSERT |
no | Log new and unresolved results instead of throwing. Default false |
httpTimeoutInSeconds |
VRT_HTTPTIMEOUTINSECONDS |
no | How long to wait for each response. Default 360, because the server compares images while it handles the request; 0 means no limit. Connecting has its own 10 second limit |
Each option is taken from the builder (or constructor) first, then the environment variable, then vrt.json in the working directory (or the file passed to configFile).
VisualRegressionTrackerConfig config = VisualRegressionTrackerConfig.builder()
.apiUrl("http://localhost:4200")
.apiKey("F5Z2H0H2SNMXZVHX0EA4YQM1MGDD")
.project("Default project")
.branchName("feature/login")
.sourceBranch("develop")
.enableSoftAssert(true)
.build();export VRT_APIURL=http://localhost:4200
export VRT_APIKEY=F5Z2H0H2SNMXZVHX0EA4YQM1MGDD
export VRT_PROJECT="Default project"
export VRT_BRANCHNAME=feature/login
export VRT_SOURCEBRANCH=develop
export VRT_CIBUILDID=40bdba4
export VRT_ENABLESOFTASSERT=true{
"apiUrl": "http://localhost:4200",
"apiKey": "F5Z2H0H2SNMXZVHX0EA4YQM1MGDD",
"project": "Default project",
"branchName": "feature/login",
"sourceBranch": "develop",
"ciBuildId": "40bdba4",
"enableSoftAssert": true
}Keep the API key out of git: commit a vrt.json.example and ignore vrt.json.
VisualRegressionTracker vrt = new VisualRegressionTracker(config);
// or new VisualRegressionTracker() to read the environment and vrt.json only
vrt.start();
try {
// Selenium: a file, bytes or base64
Path screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE).toPath();
vrt.track("Login page", screenshot, TestRunOptions.builder()
.browser("Chrome")
.os("Linux")
.viewport("1280x720")
.build());
} finally {
vrt.stop();
}start() creates a build; every track call adds a test run to it; stop() finishes the build.
| Call | Upload |
|---|---|
track(name, Path image) |
multipart, file name kept |
track(name, byte[] image) |
multipart |
track(name, String imageBase64) |
base64 in JSON |
Each also takes a TestRunOptions as the third argument. Prefer a file or bytes: base64 in JSON is bound by the server's BODY_PARSER_JSON_LIMIT (5 MB in the server's shipped .env, Express's 100 kB when unset), multipart by the much larger MULTIPART_FILE_SIZE_LIMIT (100 MB by default).
| Option | Description |
|---|---|
os, browser, viewport, device, customTags |
Part of the test's identity: a baseline is matched on name and all of these |
diffTollerancePercent |
Difference, in percent, still treated as a match (the spelling is the server's) |
ignoreAreas |
List of IgnoreAreas (x, y, width, height) left out of the comparison |
comment |
Comment shown on the test run |
merge |
Approving this test run in the UI updates the main branch's baseline instead of branchName's |
track returns a TestRunResult:
getTestRunResponse():getId(),getStatus(),getUrl()(the test run in the VRT UI),getDiffPercent()and the rest of the server's answer;getImageUrl(),getBaselineUrl(),getDiffUrl(): the images, served through<apiUrl>/images/<name>;nullwhen there is no such image.
| Status | track |
|---|---|
OK, APPROVED, AUTO_APPROVED, FAILED |
returns |
NEW (no baseline yet) |
throws TestRunException "No baseline: ", or logs it with enableSoftAssert |
UNRESOLVED (difference found) |
throws TestRunException "Difference found: ", or logs it with enableSoftAssert |
UNKNOWN (a status this SDK does not know) |
logs a warning and returns |
When the server rejects a request, the SDK throws TestRunException. getStatusCode() holds the HTTP status and the message reads Request failed with status <code>: <server's message>. Calling track or stop before start also throws it, with no status code.
| Status | Meaning |
|---|---|
| 401, 403 | Wrong apiKey, or the user may not use the project |
| 404 | Project or build not found |
| 413 | Request too large. For base64 the message suggests multipart; for multipart it names MULTIPART_FILE_SIZE_LIMIT |
| 422 | Image has more pixels than the server's MAX_IMAGE_PIXELS |
| 503 | Server busy. With Retry-After the SDK waits and retries up to 3 times (at most 60 s per wait) before throwing |
Network failures and timeouts surface as IOException (HttpTimeoutException for a timeout).
JitPack builds the SDK from a git tag; there is nothing to upload.
- Make sure
masteris green. - Tag the commit with the version, without a
v:git tag <version> && git push origin <version>. - Create a GitHub release for the tag with the release notes.
- Open
https://jitpack.io/#Visual-Regression-Tracker/sdk-java/<version>to start the build, and check that its log ends with the artifact list.
JitPack runs ./gradlew clean -Pgroup=com.github.Visual-Regression-Tracker -Pversion=<tag> -xtest assemble publishToMavenLocal on the JDK from jitpack.yml, so the artifact version is always the tag. version in gradle.properties is only the default for local builds. CI runs the same command on Java 25 for every pull request.
Breaking changes for the next major go into next through pull requests against it. Merge master into next when master gets new commits.
To release a candidate, make sure next is green, then gh release create X.Y.Z-rc.N --target next --prerelease --notes-file <notes> (for example 6.0.0-rc.1) tags next and creates the pre-release; then start the JitPack build as in step 4. Projects get it only by asking for that exact version.
To release the final version, merge next into master with a merge commit, then tag master as above. The repository deletes merged branches, so create next again from master before the next round of candidates.