Test Script Protocol
Learning Objectives
- Understand when and why to use Test Scripts
- Write test cases using
case()andgroup() - Use expect matchers for assertions
- 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
--!strict
-- 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)
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)
--!strict
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
--!strict
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
--!strict
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
--!strict
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 Scripts
local MyNodeScript = require('MyNodeScript') -- Error!
-- CORRECT: Only require Util Scripts
local MyUtil = require('MyUtil') -- Works! -
Forgetting to use expect in case callback
-- WRONG: Not using the expect parameter
case('Test', function()
local result = MyUtil.add(2, 3)
-- No assertion! Test always passes
end)
-- CORRECT: Use expect for assertions
case('Test', function(expect)
local result = MyUtil.add(2, 3)
expect(result).is(5)
end) -
Using wrong negation syntax
-- WRONG: JavaScript syntax
expect(value).not.is(5) -- Error!
expect(value).toBe(5) -- Error!
-- CORRECT: Rive syntax
expect(value).never.is(5) -- Works!
expect(value).is(5) -- Works! -
Missing return in factory function
-- WRONG: Not returning the setup function
return function(): Tests
-- Missing return!
end
-- CORRECT: Return the setup function
return function(): Tests
return setup
end
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 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