Oorian Testing Guide
1. Headless Page Rendering
Oorian's headless rendering infrastructure lets you test page output without deploying to a servlet container. The OorianTestHarness class in com.oorian.test provides a complete mock environment for rendering HttpFile subclasses (including HtmlPage) and capturing the generated HTML.
Setting Up the Test Harness
OorianTestHarness is the entry point for headless rendering. Call setup() before your tests to initialize the mock environment, and teardown() after your tests to clean up.
OorianTestHarness harness = new OorianTestHarness();
harness.setup();
// ... run tests ...
harness.teardown();
Rendering Pages
The harness provides two render methods. The simple version takes an HttpFile and a path string, returning the rendered HTML directly. The advanced version accepts a TestHttpRequest for full control over request parameters, headers, and cookies, and returns a TestHttpResponse with access to the status code, headers, and content.
// Simple render — returns HTML string
String html = harness.render(new HomePage(), "/");
// Render with custom request
TestHttpRequest request = harness.createRequest("/users/123");
request.addParameter("tab", "profile");
TestHttpResponse response = harness.render(new UserPage(), request);
String html = response.getContent();
int status = response.getStatus();
OorianTestHarness API
| Method | Description |
|---|---|
setup() |
Initializes the test environment (mock context, session) |
teardown() |
Cleans up the test environment |
render(HttpFile, String) |
Renders a page at the given path, returns HTML string |
render(HttpFile, TestHttpRequest) |
Renders with a custom request, returns TestHttpResponse |
createRequest(String) |
Creates a pre-configured test request for the given path |
createResponse() |
Creates an empty test response |
getSession() |
Returns the shared test session |
resetSession() |
Creates a new empty session (for test isolation) |
Mock Classes
| Class | Implements | Key Features |
|---|---|---|
TestAppContext |
AppContext |
In-memory attributes and init parameters, configurable context path |
TestHttpSession |
OorianHttpSession |
In-memory attributes, auto-generated session ID, invalidation tracking |
TestHttpRequest |
OorianHttpRequest |
Configurable path, parameters, headers, cookies, locale, body content |
TestHttpResponse |
OorianHttpResponse |
Captures content, status, headers, cookies, redirect URL |
Session Persistence
The test harness maintains a single session across renders within a test setup, simulating real user behavior where a session persists as the user navigates between pages. Call resetSession() to create a fresh session for test isolation.
// Session persists across renders
harness.render(new LoginPage(), "/login");
harness.getSession().setAttribute("user", testUser);
String html = harness.render(new DashboardPage(), "/dashboard");
// DashboardPage can access the "user" attribute
// Reset for a clean session
harness.resetSession();
2. Mock WebSocket Client
For testing server-side components that use WebSocket communication, Oorian provides mock implementations in the com.oorian.testing package. These mocks let you verify WebSocket messages, session attributes, and request handling without a servlet container or browser.
MockWebsocketConnection
Implements WebsocketConnection. Stores all sent messages in an ordered list for verification. Throws IllegalStateException if messages are sent after close.
| Method | Description |
|---|---|
sendMessage(String) |
Stores message for later retrieval |
close() |
Marks connection as closed |
getMessages() |
Returns unmodifiable list of all sent messages |
getLastMessage() |
Returns the most recently sent message |
getMessageCount() |
Returns the number of messages sent |
isClosed() |
Returns whether the connection has been closed |
clearMessages() |
Resets the message history |
MockWebsocketConnection ws = new MockWebsocketConnection();
ws.sendMessage("hello");
ws.sendMessage("world");
ws.getMessageCount(); // 2
ws.getLastMessage(); // "world"
ws.getMessages(); // ["hello", "world"]
ws.close();
ws.isClosed(); // true
// ws.sendMessage("fail"); → IllegalStateException
MockOorianHttpSession
Implements OorianHttpSession. Uses an in-memory HashMap for attributes. Supports invalidation — all methods throw IllegalStateException after invalidate().
| Method | Description |
|---|---|
MockOorianHttpSession() |
Creates session with random UUID |
MockOorianHttpSession(String) |
Creates session with specified ID |
setId(String) |
Changes the session ID |
setNew(boolean) |
Sets the new-session flag |
isInvalidated() |
Returns whether session was invalidated |
getAttributeMap() |
Returns unmodifiable view of all attributes |
MockOorianHttpRequest
Implements OorianHttpRequest. All properties are configurable via fluent setters that return this for chaining.
| Property | Default |
|---|---|
| Request URI | / |
| Context Path | "" (empty) |
| Method | GET |
| Locale | Locale.US |
| Remote Address | 127.0.0.1 |
| Server Name | localhost |
| Scheme | http |
| Server Port | 8080 |
MockOorianHttpRequest request = new MockOorianHttpRequest()
.setRequestURI("/app/users/123")
.setContextPath("/app")
.setMethod("POST")
.setContentType("application/json")
.setBody("{\"name\": \"John\"}");
request.addHeader("Authorization", "Bearer token123");
request.addParameter("page", "1");
OorianTestContext
Convenience class that creates and wires together all mock objects, then registers the session to the current thread so OorianSession.get() works in tests.
Factory methods:
| Method | Description |
|---|---|
create() |
Creates context with default mocks |
create(Locale) |
Creates context with specified locale on the request |
create(String, String) |
Creates context with specified URI and HTTP method |
Accessor methods:
| Method | Description |
|---|---|
getHttpSession() |
Returns the MockOorianHttpSession |
getRequest() |
Returns the MockOorianHttpRequest |
getWebsocketConnection() |
Returns the MockWebsocketConnection |
getOorianSession() |
Returns the registered OorianSession |
tearDown() |
Removes session from current thread (call in test cleanup) |
OorianTestContext ctx = OorianTestContext.create();
try
{
OorianSession session = OorianSession.get();
session.setAttribute("userId", 12345);
MockWebsocketConnection ws = ctx.getWebsocketConnection();
// ... trigger server-push logic ...
List<String> messages = ws.getMessages();
}
finally
{
ctx.tearDown(); // Clean up thread-local
}
3. Snapshot Testing
Snapshot testing detects unintended changes in component rendering by comparing current output against saved baselines. The SnapshotTester class in com.oorian.testing saves rendered HTML to .snap files on first run and verifies against those baselines on subsequent runs.
How It Works
The snapshot testing workflow follows three steps:
- First run: No
.snapfile exists — the current output is saved as the baseline. - Subsequent runs: Output is compared against the saved snapshot.
- If they match, the test passes. If they differ, a
SnapshotMismatchExceptionis thrown.
SnapshotTester API
| Method | Description |
|---|---|
SnapshotTester(String) |
Creates tester with snapshot directory path |
SnapshotTester(Path) |
Creates tester with snapshot directory path |
assertMatchesSnapshot(String, Element) |
Renders element and compares against saved snapshot |
assertMatchesSnapshot(String, String) |
Compares raw string against saved snapshot |
updateSnapshot(String, Element) |
Saves element's rendered HTML as new baseline |
updateSnapshot(String, String) |
Saves string as new baseline |
snapshotExists(String) |
Checks if a snapshot file exists |
deleteSnapshot(String) |
Deletes a snapshot file |
setUpdateMode(boolean) |
When true, overwrites mismatched snapshots instead of failing |
SnapshotResult
| Value | Description |
|---|---|
MATCH |
Current output matches the saved snapshot |
MISMATCH |
Current output differs (thrown as exception unless update mode) |
NEW_SNAPSHOT |
No snapshot existed; baseline was saved |
Basic Usage
SnapshotTester snapshots = new SnapshotTester("test/snapshots");
// Build a component
Div card = new Div();
card.addClass("user-card");
card.addElement(new H1("John Doe"));
card.addElement(new Paragraph("john@example.com"));
// First run: creates test/snapshots/user-card.snap
// Subsequent runs: compares against saved snapshot
snapshots.assertMatchesSnapshot("user-card", card);
Update Mode
When you intentionally change a component's output, enable update mode to refresh baselines instead of failing.
SnapshotTester snapshots = new SnapshotTester("test/snapshots");
snapshots.setUpdateMode(true);
// Overwrites saved snapshot with current output
snapshots.assertMatchesSnapshot("user-card", card);
Handling Mismatches
try
{
snapshots.assertMatchesSnapshot("header", header);
}
catch (SnapshotMismatchException e)
{
String expected = e.getExpected(); // saved content
String actual = e.getActual(); // current content
String name = e.getSnapshotName(); // "header"
}
4. Complete Example
Putting it all together. The following test class demonstrates headless page rendering, mock WebSocket verification, and snapshot testing used in combination.
public class HomePageTest
{
private OorianTestHarness harness;
private OorianTestContext context;
private SnapshotTester snapshots;
public void setup()
{
harness = new OorianTestHarness();
harness.setup();
context = OorianTestContext.create();
snapshots = new SnapshotTester("test/snapshots");
}
public void teardown()
{
context.tearDown();
harness.teardown();
}
public void testHomePageRenders()
{
// Render the page
String html = harness.render(new HomePage(), "/");
// Verify the HTML matches the saved snapshot
snapshots.assertMatchesSnapshot("home-page", html);
}
public void testHomePageWithSession()
{
// Set up session state
OorianSession.get().setAttribute("user", "John");
// Render with session context
String html = harness.render(new HomePage(), "/");
// Verify personalized content
snapshots.assertMatchesSnapshot("home-page-logged-in", html);
}
public void testWebSocketMessages()
{
// Render the page
harness.render(new DashboardPage(), "/dashboard");
// Verify WebSocket messages sent during render
MockWebsocketConnection ws = context.getWebsocketConnection();
assert ws.getMessageCount() > 0;
}
public void testCustomRequest()
{
// Create a custom request
TestHttpRequest request = harness.createRequest("/search");
request.addParameter("q", "oorian");
request.addHeader("Accept-Language", "de-DE");
// Render with custom request
TestHttpResponse response = harness.render(new SearchPage(), request);
// Verify response
assert response.getStatus() == 200;
snapshots.assertMatchesSnapshot("search-results", response.getContent());
}
}
API Summary
| Class | Package | Purpose |
|---|---|---|
OorianTestHarness |
com.oorian.test |
Headless page rendering and lifecycle |
TestAppContext |
com.oorian.test |
Mock application context |
TestHttpSession |
com.oorian.test |
Mock HTTP session |
TestHttpRequest |
com.oorian.test |
Mock HTTP request |
TestHttpResponse |
com.oorian.test |
Mock HTTP response |
MockWebsocketConnection |
com.oorian.testing |
Mock WebSocket connection |
MockOorianHttpSession |
com.oorian.testing |
Mock session with invalidation |
MockOorianHttpRequest |
com.oorian.testing |
Configurable mock request |
OorianTestContext |
com.oorian.testing |
Wires mocks and registers session |
SnapshotTester |
com.oorian.testing |
Snapshot save/load/compare |
SnapshotResult |
com.oorian.testing |
Enum: MATCH, MISMATCH, NEW_SNAPSHOT |
SnapshotMismatchException |
com.oorian.testing |
Assertion error with expected/actual |