Testing
Quill has first-class support for testing. No frameworks to install, no configuration to write. Just use test and expect.
Test blocks
A test block groups related assertions under a descriptive name:
test "basic math":
expect 2 + 2 is 4
expect 10 - 3 is 7
expect 3 * 4 is 12
When you run this, you will see:
✓ basic math
1 passed, 0 failed
If a test fails:
✗ basic math: Expected (2 + 2) === 5 to be true
0 passed, 1 failed
The expect keyword
expect takes an expression that should evaluate to true. If the expression is false, the test fails with an error message.
-- Equality
expect name is "Alice"
-- Comparisons
expect age is greater than 18
expect count is less than 100
-- Membership
expect colors contains "red"
-- Function results
expect length("hello") is 5
expect upper("hello") is "HELLO"
Testing functions
Define your functions, then write tests for them:
to add a b:
give back a + b
to multiply a b:
give back a * b
to isEven n:
give back n % 2 is 0
test "addition":
expect add(2, 3) is 5
expect add(-1, 1) is 0
expect add(0, 0) is 0
test "multiplication":
expect multiply(3, 4) is 12
expect multiply(0, 100) is 0
expect multiply(-2, 3) is -6
test "even numbers":
expect isEven(4)
expect not isEven(3)
Running tests
There are two ways to run tests:
Option 1: quill run
Run your test file like any Quill program. This runs all code in the file, including non-test code:
quill run tests.quill
Option 2: quill test (recommended)
Use the dedicated test command that only runs test blocks and skips everything else. This is the best way to run tests because it gives you a clean summary:
quill test tests.quill
You can also test all files in a directory:
# Test a specific file
quill test math_test.quill
# Test all _test.quill files in the current directory
quill test
# Test all files matching a pattern
quill test tests/
Example output:
✓ addition
✓ multiplication
✓ even numbers
3 passed, 0 failed
Watch Mode
Use --watch (or -w) to automatically re-run your tests every time you save a file:
quill test --watch
quill test math.quill -w
This is perfect for test-driven development — write a test, save, and instantly see if it passes.
Async tests
If your code uses await (for example, fetching data from an API or reading a file), your tests can use await too. Just write await inside the test block as you normally would:
to fetchUser id:
response is await fetch("https://api.example.com/users/{id}")
give back await response.json()
test "fetch user returns valid data":
user is await fetchUser(1)
expect user.name is not nothing
expect user.id is 1
test "fetch user handles errors":
try:
user is await fetchUser(-1)
expect user is nothing
if it fails error:
expect error is not nothing
Best practices
- Give each test a descriptive name that explains what it tests
- Keep tests small and focused -- one concept per test block
- Test edge cases: zero, negative numbers, empty strings, empty lists
- Place test files alongside your code with a
_testsuffix (e.g.,math_test.quill)
Organizing tests
As your project grows, keep your tests organized so they are easy to find and maintain:
Group related tests by feature
Give each test file a clear name that matches the file it tests:
-- File structure:
-- math.quill (your code)
-- math_test.quill (tests for math.quill)
-- strings.quill (your code)
-- strings_test.quill (tests for strings.quill)
Use descriptive test names
A good test name describes the scenario and expected result:
-- Bad: vague names
test "test 1":
expect add(2, 3) is 5
-- Good: descriptive names
test "add returns the sum of two positive numbers":
expect add(2, 3) is 5
test "add handles negative numbers":
expect add(-1, -2) is -3
test "add with zero returns the other number":
expect add(0, 5) is 5
expect add(5, 0) is 5
Test one thing at a time
Each test block should focus on a single behavior. If a test fails, you want to know exactly what broke:
-- Good: separate tests for separate behaviors
test "sort puts numbers in ascending order":
expect sort([3, 1, 2]) is [1, 2, 3]
test "sort handles empty list":
expect sort([]) is []
test "sort handles single item":
expect sort([42]) is [42]
A complete test file
-- string_test.quill
test "upper and lower":
expect upper("hello") is "HELLO"
expect lower("HELLO") is "hello"
test "trim":
expect trim(" hi ") is "hi"
test "startsWith and endsWith":
expect startsWith("hello", "he")
expect endsWith("hello", "lo")
expect not startsWith("hello", "lo")
test "replace":
expect replace_text("hello world", "world", "Quill") is "hello Quill"
test "split and join":
words are split("a,b,c", ",")
expect length(words) is 3
expect join(words, "-") is "a-b-c"