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 step take-screenshot]]))
(deftest simple-navigation-test
(with-serenity [page]
(step "Navigate to example.com"
#(do
(.navigate page "https://example.com")
(take-screenshot page "homepage")))
(step "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"))
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
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:
(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 - Navigate and capture screenshots
(step "Navigate to application homepage"
#(do
(.navigate page "https://example.com")
(.waitForLoadState page LoadState/NETWORKIDLE)
(take-screenshot page "01-homepage")))
(step "Verify page loaded correctly"
#(do
(is (clojure.string/includes? (.title page) "Example"))
(take-screenshot page "02-verified")))
;; 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 based on API data
(step "Navigate to details page"
#(do
(.navigate page "https://example.com/details")
(take-screenshot page "03-details-page")))))
This test will generate a Serenity report that includes:
Create comprehensive screenshot galleries to document visual test journeys:
(deftest visual-journey-test
(with-serenity [page]
(step "Initial page load"
#(do
(.navigate page "https://example.com")
(take-screenshot page "01-initial-load")))
(step "User interaction - Login form"
#(do
(.fill page "#username" "testuser")
(.fill page "#password" "password")
(take-screenshot page "02-login-filled")
(.click page "#login-button")
(take-screenshot page "03-after-login")))
(step "Navigate through application sections"
#(do
(.click page "a:has-text(\"Dashboard\")")
(take-screenshot page "04-dashboard")
(.click page "a:has-text(\"Profile\")")
(take-screenshot page "05-profile")
(.click page "a:has-text(\"Settings\")")
(take-screenshot page "06-settings")))
(step "Final state verification"
#(do
(.click page "#logout")
(take-screenshot page "07-logged-out")))))
Result: Serenity generates a screenshot gallery showing the complete user journey with 7+ images, all clickable 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 and api-step call with timing and statustake-screenshot with thumbnails and galleriesReport Structure:
target/site/serenity/
├── index.html # Main dashboard
├── *.json # Test result data
├── *.png # Screenshots
├── 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 |