Test Script Protocol
Learning Objectives
- Understand when and why to use Test Scripts
- Write test cases using
case()andgroup() - Use expect matchers for assertions
- Resolve Blob fixtures through the current
Tester.blobhelper - Run and interpret test results in the Rive editor
AE/JS Syntax Comparison
| Concept | JavaScript (Jest/Vitest) | Luau (Rive Test Scripts) |
|---|---|---|
| Test case | test('name', () => {}) | case('name', function(expect) end) |
| Test group | describe('name', () => {}) | group('name', function() end) |
| Equality | expect(x).toBe(y) | expect(x).is(y) |
| Negation | expect(x).not.toBe(y) | expect(x).never.is(y) |
| Greater than | expect(x).toBeGreaterThan(y) | expect(x).greaterThan(y) |
Test Scripts test Util Scripts only. They're designed for testing pure functions and reusable logic modules, not Node Scripts or other script types.
Overview
Test Scripts enable unit testing for Util Scripts directly in the Rive editor. They provide a simple testing framework with assertions, making it easy to verify your utility functions work correctly.
When to use Test Scripts:
- Verify Util Script logic works correctly
- Test edge cases and error conditions
- Regression testing after changes
- Document expected behavior through tests
The Test Script Template
-- Load the Util(s) you want to test
local MyUtil = require('MyUtil')
function setup(test: Tester)
local case = test.case
local group = test.group
-- Single test case
case('Addition works', function(expect)
local result = MyUtil.add(2, 3)
expect(result).is(5)
end)
-- Group related tests
group('Math operations', function()
case('Multiplication', function(expect)
expect(MyUtil.multiply(3, 4)).is(12)
end)
case('Division', function(expect)
expect(MyUtil.divide(10, 2)).is(5)
end)
end)
end
return function(): Tests
return setup
end
Tester API
test.case(name, fn) — Define a Test
Creates a single test case. The callback receives an expect function for assertions.
case('Should calculate area', function(expect)
local area = MyUtil.rectangleArea(5, 10)
expect(area).is(50)
end)
test.group(name, fn) — Group Related Tests
Organizes related tests together. Groups can be nested for better organization.
group('Rectangle functions', function()
case('Area calculation', function(expect)
expect(MyUtil.rectangleArea(5, 10)).is(50)
end)
case('Perimeter calculation', function(expect)
expect(MyUtil.rectanglePerimeter(5, 10)).is(30)
end)
-- Nested group
group('Edge cases', function()
case('Zero dimensions', function(expect)
expect(MyUtil.rectangleArea(0, 10)).is(0)
end)
end)
end)
test.blob(name) — Resolve a Blob Fixture
The current editor testing reference exposes blob as a function-valued
Tester property. It returns nil when the fixture is missing.
function setup(test: Tester)
test.case("fixture is packaged", function(expect)
local fixture = test.blob("SampleData")
expect(fixture ~= nil).is(true)
end)
end
This is current editor/test-host evidence. A successful editor test does not
prove that an exported runtime uses the same asset name; validate the runtime
consumer with context:blob(name) separately.
The released source contains RASC workspace/test messages and an in-memory
compiler/bake lane. Those messages are tooling internals. Test scripts still
return function(): Tests, receive Tester, and use test.case,
test.group, and current test.blob; they do not call the workspace protocol
or consume serialized host records.
Expect Matchers
Equality
expect(value).is(expected) -- value == expected
expect(value).never.is(wrong) -- value ~= wrong
Numeric Comparisons
expect(value).greaterThan(n) -- value > n
expect(value).greaterThanOrEqual(n) -- value >= n
expect(value).lessThan(n) -- value < n
expect(value).lessThanOrEqual(n) -- value <= n
Negation with .never
Any matcher can be negated by chaining .never:
expect(10).never.is(5) -- PASS: 10 ~= 5
expect(10).never.greaterThan(5) -- FAIL: 10 > 5
expect(3).never.greaterThan(5) -- PASS: 3 is not > 5
Running Tests
- Right-click your Test script in the Assets panel
- Select "Run Tests"
- Results display:
- Passing and failing cases listed
- Cases highlighted in the script editor
- Error messages for failures
Practice Exercises
Exercise 1: Basic Test Case ⭐
Premise
Writing clear, focused test cases is the foundation of good testing. Each case should test one specific behavior.
By the end of this exercise, you will write a test case that verifies a simple clamp function.
Use Case
You have a Util Script with a clamp function that limits a value to a range. You want to verify it works correctly.
Setup
In Rive Editor:
-
Create the Util Script first:
- Assets panel →
+→ Script → Blank Script (this is how you create a Util Script) - Name it
MathUtils - Add a clamp function
- Assets panel →
-
Create the Test Script:
- Assets panel →
+→ Script → Test Script - Name it
Exercise1_BasicTest
- Assets panel →
Util Script (MathUtils)
local MathUtils = {}
function MathUtils.clamp(value: number, min: number, max: number): number
if value < min then return min end
if value > max then return max end
return value
end
return MathUtils
Starter Code
local MathUtils = require('MathUtils')
function setup(test: Tester)
local case = test.case
case('Clamp returns minimum when value is too low', function(expect)
-- TODO: Test that clamp(-5, 0, 10) returns 0
local result = MathUtils.clamp(-5, 0, 10)
-- Use expect(result).is(expected)
print(`ANSWER: result={result}`)
end)
end
return function(): Tests
return setup
end
Assignment
- Add
expect(result).is(0)to verify the clamp result - Run the test to see output
Expected Output
Your console output should display the clamped result.
Verify Your Answer
Checklist
- Used
expect(result).is(expected)for assertion - Test verifies minimum clamping behavior
- Test passes when run
Exercise 2: Test Groups ⭐⭐
Premise
Organizing tests into groups makes large test suites easier to understand and maintain. Related tests should be grouped together.
By the end of this exercise, you will organize multiple test cases into logical groups.
Use Case
You're testing a comprehensive MathUtils module with multiple functions. Grouping keeps tests organized.
Setup
In Rive Editor:
-
Use the same MathUtils from Exercise 1
-
Create the Test Script:
- Assets panel →
+→ Script → Test Script - Name it
Exercise2_TestGroups
- Assets panel →
Starter Code
local MathUtils = require('MathUtils')
function setup(test: Tester)
local case = test.case
local group = test.group
-- TODO: Create a group called 'clamp function'
-- Inside, add three test cases:
-- 1. 'clamps to minimum' - test clamp(-5, 0, 10) returns 0
-- 2. 'clamps to maximum' - test clamp(15, 0, 10) returns 10
-- 3. 'returns value when in range' - test clamp(5, 0, 10) returns 5
group('clamp function', function()
case('clamps to minimum', function(expect)
local result = MathUtils.clamp(-5, 0, 10)
expect(result).is(0)
end)
-- TODO: Add 'clamps to maximum' case
-- TODO: Add 'returns value when in range' case
end)
print("ANSWER: group=clamp function,cases=3")
end
return function(): Tests
return setup
end
Assignment
- Complete the two remaining test cases inside the group
- Verify all three tests pass
Expected Output
Your console output should confirm the group structure.
Verify Your Answer
Checklist
- All three test cases are inside the group
- Each case tests a different clamp behavior
- Tests pass when run
Exercise 3: Using .never for Negation ⭐⭐
Premise
Sometimes you need to verify that a value is NOT something. The .never modifier inverts any matcher.
By the end of this exercise, you will use .never to assert negative conditions.
Use Case
You want to verify that certain operations do NOT produce specific values.
Setup
In Rive Editor:
- Create the Test Script:
- Assets panel →
+→ Script → Test Script - Name it
Exercise3_NeverMatcher
- Assets panel →
Starter Code
local MathUtils = require('MathUtils')
function setup(test: Tester)
local case = test.case
case('Clamped value is never negative', function(expect)
local result = MathUtils.clamp(-100, 0, 10)
-- TODO: Assert that result is NOT negative
-- Use: expect(result).never.lessThan(0)
print(`ANSWER: result={result},notNegative=true`)
end)
case('Clamped value never exceeds max', function(expect)
local result = MathUtils.clamp(100, 0, 10)
-- TODO: Assert that result is NOT greater than 10
-- Use: expect(result).never.greaterThan(10)
print(`ANSWER2: result={result},notOver10=true`)
end)
end
return function(): Tests
return setup
end
Assignment
- Add
expect(result).never.lessThan(0)to first case - Add
expect(result).never.greaterThan(10)to second case - Run tests to verify
Expected Output
Your console output should confirm both negative assertions.
Verify Your Answer
Checklist
- Used
.never.lessThan()for first assertion - Used
.never.greaterThan()for second assertion - Both tests pass
Knowledge Check
Common Mistakes
-
Trying to test Node Scripts
-- WRONG: Test Scripts only work with Util Scriptslocal MyNodeScript = require('MyNodeScript') -- Error!-- CORRECT: Only require Util Scriptslocal MyUtil = require('MyUtil') -- Works! -
Forgetting to use expect in case callback
-- WRONG: Not using the expect parametercase('Test', function()local result = MyUtil.add(2, 3)-- No assertion! Test always passesend)-- CORRECT: Use expect for assertionscase('Test', function(expect)local result = MyUtil.add(2, 3)expect(result).is(5)end) -
Using wrong negation syntax
-- WRONG: JavaScript syntaxexpect(value).not.is(5) -- Error!expect(value).toBe(5) -- Error!-- CORRECT: Rive syntaxexpect(value).never.is(5) -- Works!expect(value).is(5) -- Works! -
Missing return in factory function
-- WRONG: Not returning the setup functionreturn function(): Tests-- Missing return!end-- CORRECT: Return the setup functionreturn function(): Testsreturn setupend
Best Practices
- Use descriptive names for groups and cases
- Test edge cases (zero, negative, large numbers, empty strings)
- Keep tests focused on single behaviors
- Group related tests logically
- Test both success and failure conditions
Self-Assessment Checklist
- I understand that Test Scripts only test Util Scripts
- I can write test cases using
case() - I can organize tests using
group() - I can guard a Blob returned by
test.blob(name) - I know how to use expect matchers (is, greaterThan, etc.)
- I can negate assertions with
.never - I can run tests in the Rive editor
Next Steps
- Continue to Core Types
- Need a refresher? Review Quick Reference