ESC
Type to search...
S
Soli Docs

Testing Functions

Test DSL with describe/it blocks, assertions, and factory functions.

Test DSL

describe(description, def)

Define a test group. context is an alias.

test(description, def)

Define an individual test case.

it(description, def)

Alternative to test(). specify is an alias.

Example

describe("Calculator", fn() {
  test("adds numbers", fn() {
    assert_eq(add(1, 2), 3)
  })

  it("subtracts numbers", fn() {
    assert_eq(subtract(5, 3), 2)
  })

  describe("division", fn() {
    it("divides numbers", fn() {
      assert_eq(divide(10, 2), 5)
    })

    it("handles division by zero", fn() {
      # Test error handling
    })
  })
})

Setup & Teardown

before_each(def)

Run before each test

after_each(def)

Run after each test

before_all(def)

Run once before all tests

after_all(def)

Run once after all tests

pending()

Mark test as pending (not yet implemented)

skip()

Skip the current test

Assertions

assert_equal(expected, actual, message)

Asserts that two values are equal.

assert_equal(42, result, "should return 42")assert_equal("hello", str, "string should match")assert_equal(true, is_valid, "should be valid");
assert_true(value, message)

Asserts that a value is true.

assert_true(user.is_active, "user should be active")assert_true(contains(items, "test"), "should contain test item");
assert_false(value, message)

Asserts that a value is false.

assert_false(user.is_blocked, "user should not be blocked")assert_false(is_empty(items), "items should not be empty");
assert_contains(haystack, needle, message)

Asserts that a collection contains a specific value.

assert_contains(users, "admin", "should contain admin user")assert_contains([1, 2, 3], 2, "should contain 2");
assert_nil(value, message)

Asserts that a value is nil (null).

assert_nil(result.error, "should have no error")assert_nil(user.deleted_at, "should not be deleted");
assert_not_nil(value, message)

Asserts that a value is not nil.

assert_not_nil(user.id, "user should have an id")assert_not_nil(response.body, "response should have body");

Assertion Result

All assertion functions return a result hash with the following structure:

{
  "passed": true,
  "message": "description of the test",
  "expected": value_that_was_expected,
  "actual": value_that_was_actual
}
assert_not(condition)

Assert condition is false

assert_eq(a, b)

Assert a equals b

assert_ne(a, b)

Assert a does not equal b

Null & Comparison

assert_null(value)

Assert value is null

assert_not_null(value)

Assert value is not null

assert_gt(a, b)

Assert a > b

assert_lt(a, b)

Assert a < b

Advanced Assertions

assert_match(str, pattern)

Assert string matches regex

assert_contains(coll, val)

Assert collection contains value

assert_hash_has_key(h, k)

Assert hash has key

assert_json(string)

Assert string is valid JSON

Expect API

Chainable assertion API for expressive testing. Use expect(value) followed by chainable methods.

expect(value)

Creates an expectation with the given value. Chain with to_*() methods.

expect(42).to_equal(42);
expect("hello").to_contain("ell");
expect(10).to_be_greater_than(5);
expect(user).to_not_be_null();

Equality Assertions

expect(value).to_be(expected)

Asserts value is same as expected (identity)

expect(value).to_equal(expected)

Asserts value equals expected (value equality)

expect(value).to_not_be(expected)

Asserts value is not the same as expected

expect(value).to_not_equal(expected)

Asserts value does not equal expected

Null Assertions

expect(value).to_be_null()

Asserts value is null

expect(value).to_not_be_null()

Asserts value is not null

Collection/String Assertions

expect(value).to_contain(item)

Asserts array or string contains item

expect(string).to_be_valid_json()

Asserts string is valid JSON

Test Helpers

with_transaction(block)

Begin → run block → always rollback (never commits)

freeze_time(ts)

Pin datetime_now(); cleared before each test

travel_to(ts)

Alias for freeze_time

unfreeze_time()

Restore wall-clock time

Factory Functions

Factory.define(name, data)

Define a factory with a static hash or callable block. Use #{n} in strings for auto-incrementing values.

Factory.define("user", {
  "name": "Test User",
  "email": "user#{n}@test.com"
})

Factory.define("post", fn() {
  return {"title": "Post #{Factory.sequence("post")}"}
})
Factory.create(name)

Create an instance from a factory.

user = Factory.create("user")
println(user["name"])  # "Test User"
Factory.create_with(name, overrides)

Create an instance with custom overrides.

admin = Factory.create_with("user", { "role": "admin" })
println(admin["role"])  # "admin"
println(admin["name"])  # "Test User" (from factory)
Factory.create_list(name, count)

Create multiple instances

Factory.sequence(name)

Get auto-incrementing number

Factory.bind(name, Model)

Associate factory with a model class

Factory.insert(name, overrides?)

Build + Model.create

Factory.clear

Clear definitions, bindings, sequences

View Introspection (E2E)

After a get/post/… request that renders a view, these helpers inspect what was rendered. They report nothing for a redirect or a JSON response. See the testing guide for the full E2E flow.

assigns()

All view locals as a hash ({} if none)

assign(key)

One view local, or null

view_path()

Rendered template path, e.g. "posts/index.html" ("" if none)

render_template()

Did the response render a view? (render_template?() is an alias)

Very large locals (≈48 KB+) degrade to a keys-only assigns(): keys present, values null.

Query Assertions (E2E)

Every response records the AQL queries the request ran (from the same log behind the dev bar's N+1 badge). Assert against it to catch a query issued in a loop, or to hold an endpoint to a query budget. See the testing guide.

assert_no_n_plus_one(res)

Fails if any AQL template fired 2+ times (a loop that should be batched)

assert_query_count(res, n)

Asserts the request ran exactly n queries

assert_max_queries(res, n)

Asserts the request ran at most n queries

res["query_count"] / res["n_plus_one"]

Raw count and {query, count} groups behind the assertions

soli test --fail-on-n1

Suite-wide guard: fails any request spec that triggers an N+1, no per-test assert_no_n_plus_one needed

Browser Testing

Available in browser specs, run with soli test --browser. Positive assertions wait for the condition; negative ones check immediately. Pass {"timeout": 30} to override the 10-second default. See Browser Testing for the full guide.

visit(path)

Navigate the browser; relative paths hit this worker's test server

click(selector)

Dispatch a real mouse click at the element's position

click_button(label)

Click a button by its label or value

fill_in(field, value)

Type into a field found by selector, label, name or placeholder

select_option(select, option)

Choose an option by value or visible text

check(box) / uncheck(box)

Tick or untick a checkbox

choose(radio)

Select a radio button

press(key)

Press a key or chord, e.g. Enter or Alt+d

page_text() / page_html()

The page's visible text, or its full markup

page_path() / page_url() / page_title()

Where the browser is, and what the page is called

evaluate(expression)

Run JavaScript in the page and get the value back, types preserved

viewport(preset) / viewport(width, height)

The size the page renders at. Declared in a describe body it covers every test in the suite (nested suites inherit it); called inside a test it resizes there and then. Presets: mobile, iphone, iphone_se, android, tablet, ipad, laptop, desktop, wide — the device ones also emulate pixel ratio and touch. No arguments reads the current viewport back. Default 1280×800

screenshot(path)

Write a PNG of the current view

wait_for(selector)

Block until an element exists

wait_for_text(text)

Block until the text appears

assert_text(text)

Waits for the page to show text; assert_no_text checks immediately

assert_selector(selector)

Waits for the element; assert_no_selector checks immediately

assert_page_path(path)

Waits until the browser is at path

assert_no_page_errors()

Fails if the page threw or logged an error

page_errors()

The captured JavaScript errors, as an array