Skip to main content

Writing Tests

A Smartest test is a named block:

test("adds numbers") do
expect(1 + 2).to eq(3)
end

The name should describe the behavior being checked. If an expectation fails or the block raises an ordinary exception, the test fails.

Editor Support

Smartest ships RBS signatures for the public DSL, including around_suite, around_test, fixture, suite_fixture, on_teardown, expect, and the hook registration methods. Type-aware editors such as RubyMine can use those signatures for completion and documentation. The test method is intentionally not defined in RBS because it conflicts with Ruby's built-in Kernel#test file-test helper in some editors.

For Playwright page fixtures, add a RubyMine type comment immediately before the test:

# @type [Playwright::Page] page
test("opens the home page") do |page:|
page.goto("/")
end

Assertions

Smartest uses an expectation style:

expect(actual).to eq(expected)
expect(actual).not_to eq(expected)

Examples:

test("strings") do
expect("hello").to eq("hello")
end

test("arrays") do
expect([1, 2, 3]).to include(2)
end

test("URLs") do
expect("about:blank").to start_with("about:")
end

test("downloads") do
expect("screenshot.png").to end_with(".png")
end

test("types") do
expect("smartest").to be_a(String)
end

test("nil values") do
expect(nil).to be_nil
end

test("messages") do
expect("timeout after 1000ms").to match(/timeout/)
end

test("events") do
expect(%i[request close open]).to contain_exactly(:open, :request, :close)
end

Block expectations use Ruby blocks:

test("raises an error") do
expect { Integer("not a number") }.to raise_error(ArgumentError)
expect { raise "request timed out" }.to raise_error(/timed out/)
expect { Integer("not a number") }.to raise_error(ArgumentError, /invalid/)
end

test("changes state") do
count = 0

expect { count += 1 }.to change { count }.from(0).to(1).by(1)
end

Use Skipping Tests when a test should be skipped under a runtime condition or marked as an expected failure with pending.

Requesting Fixtures

Tests request fixtures with required keyword arguments:

test("uses a user") do |user:|
expect(user.name).to eq("Alice")
end

The keyword name is the fixture name. Smartest resolves :user before calling the test block.

Positional parameters are intentionally rejected:

test("bad") do |user|
# Not supported.
end

Use the keyword form instead:

test("good") do |user:|
expect(user.name).to eq("Alice")
end

Organizing Test Files

The recommended layout keeps fixtures in their own files and tests in files ending with _test.rb:

smartest/
test_helper.rb
fixtures/
app_fixture.rb
matchers/
predicate_matcher.rb
example_test.rb

Fixture files under smartest/fixtures/ and matcher files under smartest/matchers/ are loaded by the generated smartest/test_helper.rb. Register fixture classes from an around_suite or around_test block with use_fixture.

Example:

smartest/example_test.rb
require "test_helper"

test("user name") do |user:|
expect(user.name).to eq("Alice")
expect(user).to be_active
end

Smartest does not currently provide describe or context blocks. Prefer clear test names and small files while the MVP API stays focused.