Packages

Multi-surface application runtime for Elixir. One TEA module renders to terminal, browser (LiveView), SSH, and MCP (agents). 30+ widgets, flexbox + CSS grid, AI agent runtime, distributed swarm with CRDTs, time-travel debugging, session recording, sandboxed REPL, and agentic commerce.

Current section

Files

Jump to
raxol docs testing README.md
Raw

docs/testing/README.md

# Testing Guide
Complete guide to testing in Raxol, covering unit tests, integration tests, component tests, and best practices.
## Quick Reference
- [Writing Tests](#writing-tests) - Basic test structure and patterns
- [Component Testing](#component-testing) - Testing UI components
- [Troubleshooting](#troubleshooting) - Common issues and solutions
## Writing Tests
### Basic Test Structure
```elixir
defmodule Raxol.Feature.Test do
use ExUnit.Case, async: true
import Mox
setup :verify_on_exit!
defmock(FeatureMock, for: Raxol.Feature.Behaviour)
setup do
{:ok, %{test_data: "value"}}
end
describe "feature_name" do
test "specific test case", %{test_data: data} do
# Test implementation
end
end
end
```
### Test Categories
- **Unit Tests**: Test individual functions and modules
- **Integration Tests**: Test interactions between components
- **Performance Tests**: Measure execution time and resource usage
### Best Practices
- Use descriptive test names
- Keep tests independent and isolated
- Mock external dependencies
- Clean up resources after tests
- Document test purpose and scenarios
## Component Testing
### Lifecycle Testing
```elixir
describe "Component Lifecycle" do
test "initializes with props" do
component = create_test_component(TestComponent, %{
value: "test",
counter: 5
})
assert component.state.value == "test"
assert component.state.counter == 5
end
test "mounts correctly" do
component = create_test_component(TestComponent)
{mounted, _} = simulate_lifecycle(component, &(&1))
assert mounted.state.mounted
assert_receive {:commands, [{:command, :mounted}]}
end
end
```
### State Management Testing
```elixir
describe "State Management" do
test "updates state through events" do
component = create_test_component(TestComponent, %{value: "initial"})
updated = simulate_event_sequence(component, [
%{type: :change, value: "updated"},
%{type: :change, value: "final"}
])
assert updated.state.value == "final"
assert length(updated.state.events) == 2
end
end
```
### Event Handling Testing
```elixir
describe "Event Handling" do
test "handles known events" do
component = create_test_component(TestComponent)
{updated, commands} = Unit.simulate_event(component, %{
type: :change,
value: "test"
})
assert updated.state.value == "test"
assert commands == [{:command, :value_changed}]
end
end
```
## Performance Testing
### Benchmark Structure
```elixir
test "meets performance targets" do
measurements = Benchmark.measure(fn ->
# Code to measure
end)
assert_performance_meets_targets(measurements, %{
average: 1, # ms
p95: 2, # ms
p99: 5 # ms
})
end
```
### Performance Targets
- **Event Processing**: < 1ms average, < 2ms 95th percentile
- **Screen Updates**: < 2ms average, < 5ms 95th percentile
- **Concurrent Operations**: < 5ms average, < 10ms 95th percentile
## Quality Standards
### Test Structure Standards
- Use descriptive test names
- Group related tests with `describe`
- Use setup and teardown appropriately
- Maintain test isolation
### Assertion Standards
- Use clear assertion messages
- Assert one thing per test
- Cover happy path, error cases, and edge cases
- Use custom assertions when helpful
### Documentation Standards
- Document test purpose
- Explain test scenarios
- Document test data requirements
## Troubleshooting
### Common Issues
#### 1. Mox.UnexpectedCallError
**Problem**: Mocked function called without expectation.
**Solution**:
```elixir
# Add expectation before the call
expect(FeatureMock, :function_name, fn arg1, arg2 ->
# Mock implementation
end)
```
#### 2. FunctionClauseError
**Problem**: Function called with wrong arguments.
**Solution**:
- Check argument types and patterns
- Verify function definition matches call site
- Use pattern matching appropriately
#### 3. KeyError
**Problem**: Accessing non-existent map key.
**Solution**:
```elixir
# Use Map.get with default
value = Map.get(my_map, :key, default_value)
# Or check existence first
if Map.has_key?(my_map, :key) do
# Access the key
end
```
#### 4. Unhandled Exits in on_exit
**Problem**: Cleanup operations failing.
**Solution**:
```elixir
on_exit(fn ->
try do
Supervisor.stop(supervisor_pid, :shutdown, :infinity)
catch
:exit, reason ->
Logger.error("Cleanup failed: #{inspect(reason)}")
end
end)
```
### Test Helpers
#### Common Assertions
```elixir
defmodule Raxol.Test.AssertionHelpers do
def assert_matches_any_pattern(term, patterns) do
assert Enum.any?(patterns, &match?(&1, term))
end
def assert_performance_meets_targets(measurements, targets) do
assert measurements.average <= targets.average
assert measurements.p95 <= targets.p95
assert measurements.p99 <= targets.p99
end
end
```
#### Test Setup Helpers
```elixir
defmodule Raxol.Test.SetupHelpers do
def setup_test_environment do
# Setup test environment
end
def cleanup_test_environment do
# Cleanup test environment
end
end
```
## Tools and Utilities
### Coverage
```bash
# Run tests with coverage
mix test --cover
# Generate coverage report
mix coveralls.html
```
### Quality Checks
```bash
# Run credo for code quality
mix credo
# Run dialyzer for type checking
mix dialyzer
```
### Performance Testing
```bash
# Run performance benchmarks
mix test --only performance
# Generate performance report
mix perf.report
```
## Additional Resources
- [Component Testing Guide](../components/testing.md) - Detailed component testing