A powerful, elegant testing library for Clojure that combines Serenity BDD reporting with Playwright browser automation and REST Assured API testing. Write beautiful, readable tests in pure Clojure with automatic screenshots, step-by-step reporting, and rich HTML documentation.
clojure.test - no Java interop requiredAdd to your deps.edn:
{:deps {com.alpha-prosoft/serenity-clojure {:mvn/version "1.4"}}}
Or with Leiningen (project.clj):
:dependencies [[com.alpha-prosoft/serenity-clojure "1.4"]]
Install Playwright browsers:
npx playwright install chromium
(ns my-app.tests
(:require [clojure.test :refer [deftest is]]
[testing.junit :refer [with-serenity ui-step]]))
(deftest simple-navigation-test
(with-serenity [page]
(ui-step page "Navigate to example.com"
#(.navigate page "https://example.com"))
(ui-step page "Verify page title"
#(is (clojure.string/includes? (.title page) "Example")))))
Generate reports:
clojure -M -e "(require '[testing.junit :as junit])(junit/generate-reports)"
View results:
Open target/site/serenity/index.html in your browser to see beautiful test reports with embedded screenshots.
with-serenity [page]Main test wrapper that provides:
(deftest my-test
(with-serenity [page]
;; `page` is a Playwright Page instance
;; Browser runs in headless Chromium by default
;; All events are automatically reported to Serenity
(.navigate page "https://example.com")))
step [description f]Execute a test step with automatic reporting:
(step "Navigate to homepage"
#(.navigate page "https://example.com"))
ui-step [page description f]Execute a UI step with automatic before/after screenshots:
(ui-step page "Login to application"
#(do
(.fill page "#username" "testuser")
(.fill page "#password" "password")
(.click page "#login-button")))
This creates a step hierarchy:
Login to application
├── Login to application - Before (screenshot)
└── Login to application - After (screenshot)
Perfect for:
api-step [description f]Execute an API call with automatic REST logging:
step with SerenityRest integration(api-step "Get user details"
#(-> (SerenityRest/given)
(.get "/users/1")
(.then)
(.statusCode 200)))
take-screenshot [page description]Capture and attach screenshot to current step:
(take-screenshot page "after-login")
;; => "after_login-1767163337862-1.png"
Screenshots appear in reports as:
generate-reports []Generate HTML reports from JSON test results:
(require '[testing.junit :as junit])
(junit/generate-reports)
Outputs:
========================================
Generating Serenity Reports
========================================
Source: target/site/serenity
Project: Serenity Clojure
✓ HTML reports generated
Open: target/site/serenity/index.html
========================================
serenity.properties# Project identification
serenity.project.name=My Test Project
# Output directory
serenity.outputDirectory=target/site/serenity
# Screenshot settings (manual via take-screenshot)
serenity.take.screenshots=FOR_EACH_ACTION
# Report configuration
serenity.report.json=true
serenity.report.show.step.details=true
serenity.console.colors=true
clojure -M:test -e "(require 'clojure.test)(clojure.test/run-all-tests #\"samples.*\")"
clojure -M:test -e "(require '[samples.demo-test])(clojure.test/run-tests 'samples.demo-test)"
clojure -M:it
clojure -M:aggregate-reports
Or manually:
clojure -M -e "(require '[testing.junit :as junit])(junit/generate-reports)"
# Open in browser (macOS)
open target/site/serenity/index.html
# Linux
xdg-open target/site/serenity/index.html
# Or use Python web server
python3 -m http.server 8000 --directory target/site/serenity
# Then visit http://localhost:8000
The ui-step macro automatically captures screenshots before and after each UI action, creating a visual timeline of your test:
(ns my-app.ui-test
(:require [clojure.test :refer [deftest is]]
[testing.junit :refer [with-serenity ui-step api-step]])
(:import [com.microsoft.playwright.options LoadState]))
(deftest user-login-journey
(with-serenity [page]
;; Each ui-step creates Before and After screenshots automatically
(ui-step page "Navigate to login page"
#(do
(.navigate page "https://myapp.com/login")
(.waitForLoadState page LoadState/NETWORKIDLE)))
(ui-step page "Fill in login credentials"
#(do
(.fill page "#username" "testuser")
(.fill page "#password" "password123")))
(ui-step page "Submit login form"
#(.click page "button[type='submit']"))
(ui-step page "Verify successful login"
#(do
(.waitForSelector page ".dashboard")
(is (.isVisible (.locator page ".user-profile")))))))
Report Output:
✓ Navigate to login page
├── Navigate to login page - Before (screenshot)
└── Navigate to login page - After (screenshot)
✓ Fill in login credentials
├── Fill in login credentials - Before (screenshot)
└── Fill in login credentials - After (screenshot)
✓ Submit login form
├── Submit login form - Before (screenshot)
└── Submit login form - After (screenshot)
✓ Verify successful login
├── Verify successful login - Before (screenshot)
└── Verify successful login - After (screenshot)
This creates 8 screenshots (4 steps × 2 screenshots each) documenting every state change in your UI test.
Here's a complete workflow that demonstrates how to write comprehensive tests, generate reports, and validate that all artifacts are properly created:
# 1. Clean previous test outputs
clojure -M:clean
# 2. Run your tests
clojure -M:test -e "(require '[samples.serenity-report-validation-test])(clojure.test/run-tests 'samples.serenity-report-validation-test)"
# 3. Generate HTML reports
clojure -M:aggregate-reports
# 4. View reports
open target/site/serenity/index.html
Test that combines browser automation with API calls in a single scenario, using automatic before/after screenshots for UI steps:
(ns my-app.integration-test
(:require [clojure.test :refer [deftest is]]
[testing.junit :refer [with-serenity ui-step api-step]])
(:import [net.serenitybdd.rest SerenityRest]
[io.restassured.http ContentType]
[com.microsoft.playwright.options LoadState]))
(deftest combined-ui-api-test
(with-serenity [page]
;; UI Testing with automatic before/after screenshots
(ui-step page "Navigate to application homepage"
#(do
(.navigate page "https://example.com")
(.waitForLoadState page LoadState/NETWORKIDLE)))
(ui-step page "Verify page loaded correctly"
#(is (clojure.string/includes? (.title page) "Example")))
;; API Testing - Call REST endpoints with full logging
(api-step "Fetch data from API with query parameters"
#(let [response (-> (SerenityRest/given)
(.baseUri "https://api.example.com")
(.queryParam "userId" "123")
(.when)
(.get "/data")
(.then)
(.statusCode 200)
(.extract)
(.response))]
(is (some? (-> response .jsonPath (.getString "id"))))))
;; More API testing with POST request
(api-step "Create new resource via API"
#(-> (SerenityRest/given)
(.baseUri "https://api.example.com")
(.contentType ContentType/JSON)
(.body {"name" "Test" "value" 42})
(.when)
(.post "/resources")
(.then)
(.statusCode 201)))
;; Continue UI interaction with automatic screenshots
(ui-step page "Navigate to details page"
#(.navigate page "https://example.com/details"))))
This test will generate a Serenity report that includes:
Create comprehensive screenshot galleries using ui-step for automatic before/after documentation, or use manual screenshots with the step macro for custom scenarios:
(ns my-app.visual-test
(:require [clojure.test :refer [deftest]]
[testing.junit :refer [with-serenity ui-step]]))
(deftest visual-journey-test
(with-serenity [page]
;; Each ui-step automatically creates before/after screenshots
(ui-step page "Navigate to homepage"
#(.navigate page "https://example.com"))
(ui-step page "Fill in login form"
#(do
(.fill page "#username" "testuser")
(.fill page "#password" "password")))
(ui-step page "Submit and login"
#(.click page "#login-button"))
(ui-step page "Open user dashboard"
#(.click page "a:has-text(\"Dashboard\")"))
(ui-step page "Navigate to profile section"
#(.click page "a:has-text(\"Profile\")"))
(ui-step page "View settings page"
#(.click page "a:has-text(\"Settings\")"))
(ui-step page "Logout from application"
#(.click page "#logout"))))
Result: This test creates 14 screenshots (7 UI steps × 2 screenshots each) showing the complete user journey with before/after states for every interaction. Serenity generates a screenshot gallery with all images clickable and organized by test step in the HTML report.
Focus on REST API testing with automatic request/response capture:
(deftest comprehensive-api-test
(with-serenity [page]
(api-step "GET request with headers and query params"
#(let [response (-> (SerenityRest/given)
(.baseUri "https://api.example.com")
(.header "X-API-Key" "test-key")
(.queryParam "filter" "active")
(.queryParam "limit" 10)
(.when)
(.get "/users")
(.then)
(.statusCode 200)
(.extract)
(.response))]
(is (> (count (-> response .jsonPath (.getList "data"))) 0))))
(api-step "POST request with JSON body and custom headers"
#(let [data {"name" "New User"
"email" "test@example.com"
"metadata" {"source" "test" "version" "1.0"}}
response (-> (SerenityRest/given)
(.baseUri "https://api.example.com")
(.contentType ContentType/JSON)
(.header "X-Request-ID" "req-123")
(.body data)
(.when)
(.post "/users")
(.then)
(.statusCode 201)
(.extract)
(.response))]
(is (= "New User" (-> response .jsonPath (.getString "name"))))))
(api-step "PUT request to update resource"
#(-> (SerenityRest/given)
(.baseUri "https://api.example.com")
(.contentType ContentType/JSON)
(.body {"status" "updated"})
(.when)
(.put "/users/1")
(.then)
(.statusCode 200)))
(api-step "DELETE request"
#(-> (SerenityRest/given)
(.baseUri "https://api.example.com")
(.when)
(.delete "/users/1")
(.then)
(.statusCode 204)))))
API Logging includes:
Test that combines browser automation with API calls in a single scenario:
(ns my-app.integration-test
(:require [clojure.test :refer [deftest is]]
[testing.junit :refer [with-serenity step api-step take-screenshot]])
(:import [net.serenitybdd.rest SerenityRest]
[io.restassured.http ContentType]
[com.microsoft.playwright Page$WaitForLoadStateOptions LoadState]))
(deftest combined-ui-api-test
(with-serenity [page]
;; UI Testing
(step "Navigate to application homepage"
#(do
(.navigate page "https://example.com")
(.waitForLoadState page LoadState/NETWORKIDLE)
(take-screenshot page "homepage")))
(step "Verify page loaded"
#(do
(is (clojure.string/includes? (.title page) "Example"))
(take-screenshot page "verified")))
;; API Testing
(api-step "Fetch data from API"
#(let [response (-> (SerenityRest/given)
(.baseUri "https://api.example.com")
(.when)
(.get "/data")
(.then)
(.statusCode 200)
(.extract)
(.response))]
(is (some? (-> response .jsonPath (.getString "id"))))))
;; More UI interaction based on API data
(step "Navigate to details page"
#(do
(.navigate page "https://example.com/details")
(take-screenshot page "details-page")))))
Focus on REST API testing with full request/response logging:
(deftest api-crud-test
(with-serenity [page]
(api-step "Create new resource"
#(let [data {"name" "Test" "value" 123}
response (-> (SerenityRest/given)
(.baseUri "https://api.example.com")
(.contentType ContentType/JSON)
(.body data)
(.when)
(.post "/resources")
(.then)
(.statusCode 201)
(.extract)
(.response))]
(is (some? (-> response .jsonPath (.getInt "id"))))))
(api-step "Read resource"
#(-> (SerenityRest/given)
(.baseUri "https://api.example.com")
(.when)
(.get "/resources/1")
(.then)
(.statusCode 200)))
(api-step "Update resource"
#(-> (SerenityRest/given)
(.baseUri "https://api.example.com")
(.contentType ContentType/JSON)
(.body {"name" "Updated"})
(.when)
(.put "/resources/1")
(.then)
(.statusCode 200)))
(api-step "Delete resource"
#(-> (SerenityRest/given)
(.baseUri "https://api.example.com")
(.when)
(.delete "/resources/1")
(.then)
(.statusCode 204)))))
Capture multiple screenshots to create a visual test journey:
(deftest visual-journey-test
(with-serenity [page]
(step "Step 1: Homepage"
#(do
(.navigate page "https://example.com")
(take-screenshot page "01-homepage")))
(step "Step 2: Login"
#(do
(.fill page "#username" "testuser")
(.fill page "#password" "password")
(take-screenshot page "02-before-login")
(.click page "#login-button")
(take-screenshot page "03-after-login")))
(step "Step 3: Navigate sections"
#(do
(.click page "a:has-text(\"Dashboard\")")
(take-screenshot page "04-dashboard")
(.click page "a:has-text(\"Profile\")")
(take-screenshot page "05-profile")))
(step "Step 4: Logout"
#(do
(.click page "#logout")
(take-screenshot page "06-logged-out")))))
Validate that Serenity reports are properly generated with all artifacts:
(ns my-app.validation-test
(:require [clojure.test :refer [deftest is testing]]
[clojure.java.io :as io]
[clojure.data.json :as json]
[testing.junit :refer [generate-reports]]))
(defn validate-screenshots []
"Check that screenshot files exist"
(let [output-dir "target/site/serenity"
png-files (filter #(.endsWith (.getName %) ".png")
(file-seq (io/file output-dir)))]
(println (str "Screenshots found: " (count png-files)))
(is (> (count png-files) 0) "Screenshots should be captured")))
(defn validate-json-reports []
"Verify JSON test reports have correct structure"
(let [output-dir "target/site/serenity"
json-files (filter #(.endsWith (.getName %) ".json")
(file-seq (io/file output-dir)))]
(doseq [json-file json-files]
(when (.isFile json-file)
(let [content (json/read-str (slurp json-file) :key-fn keyword)]
(is (contains? content :testSteps) "Report should have test steps")
(is (contains? content :title) "Report should have title")
;; Check for screenshots in steps
(let [screenshots (filter #(contains? % :screenshot)
(:testSteps content))]
(println (str "Screenshots in report: " (count screenshots))))
;; Check for API calls
(let [api-calls (filter #(contains? % :restQuery)
(:testSteps content))]
(println (str "API calls in report: " (count api-calls)))))))))
(defn validate-html-report []
"Check that HTML report exists"
(let [index-file (io/file "target/site/serenity/index.html")]
(is (.exists index-file) "HTML report should be generated")
(println (str "Report location: " (.getAbsolutePath index-file)))))
(deftest report-validation-test
(testing "Validate report artifacts"
(validate-screenshots)
(validate-json-reports)
(validate-html-report)))
Here's a complete workflow from writing tests to viewing reports:
# 1. Clean previous test outputs
clojure -M:clean
# 2. Run your tests
clojure -M:it
# 3. Generate HTML reports
clojure -M:aggregate-reports
# 4. View reports
open target/site/serenity/index.html
Serenity reports automatically include:
step, ui-step, and api-step call with timing and statusui-step, automatic before/after screenshots as nested child stepstake-screenshot with thumbnails and galleriesStep Hierarchy Example:
✓ Login to application (parent step)
├── Login to application - Before (child step with screenshot)
└── Login to application - After (child step with screenshot)
✓ API: Get user data (api step with full request/response)
Report Structure:
target/site/serenity/
├── index.html # Main dashboard
├── *.json # Test result data
├── *.png # Screenshots (before/after pairs)
├── serenity.css # Styling
└── serenity.js # Interactive features
Serenity Clojure follows these principles:
See LICENSE file for details.
Built with:
Made with ❤️ for the Clojure testing community
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |