Overview
Kolibri uses comprehensive testing for both frontend and backend code. Testing is a critical part of our development process and helps ensure code quality, prevent regressions, and document expected behavior.We strongly encourage comprehensive testing of all frontend and backend code.
Test-Driven Development (TDD)
We encourage using Test-Driven Development (TDD) and the Red/Green/Refactor cycle:- Red: Write a failing test first that describes the desired behavior
- Green: Write the minimum code to make the test pass
- Refactor: Clean up the code while keeping tests passing
When to Use TDD
This approach is particularly valuable for:- Bug fixes: Write a test that reproduces the bug (fails), then fix it (passes)
- Incremental feature building: Add one test at a time for each piece of functionality
- API changes: Test the interface before implementation
Why TDD Works
- Confidence: Tests prove your code works and prevent regressions
- Design: Writing tests first leads to better API design
- Documentation: Tests document how code should behave
- Debugging: Failing tests pinpoint exactly what’s broken
- Refactoring: Tests give you confidence to improve code structure
TDD Example: Bug Fix
Here’s a simplified example of the TDD workflow for fixing a bug:Step 1 - Red: Write a Failing Test
Write a failing test that demonstrates the bug:Step 2 - Green: Fix the Code
Fix the code to make the test pass:This is a simplified example. Kolibri uses its own permission system (
KolibriAuthPermissions from kolibri.core.auth.api) rather than standard DRF permission classes.Step 3 - Refactor: Clean Up
Review the fix and ensure it’s clean and maintainable. Run all tests to ensure nothing else broke.Testing Best Practices
General Principles
- Write tests for all new code: Both frontend and backend code should be tested
- Use descriptive test names: Test name should describe what it tests
- One assertion per test (when practical): Makes failures easier to diagnose
- Test edge cases: Empty lists, None values, invalid input, etc.
- Keep tests fast: Use mocks for expensive operations
- Keep tests isolated: Each test should be independent
- Tests assert behavior, not implementation: Test inputs and outputs, not internal implementation details
What to Test
Unit tests and integration tests should be written to ensure coverage of critical, brittle, complicated, or otherwise risky paths through the code and user experience. Nearly all code is amenable to testing. You should write tests for:- Component behavior and user interactions
- State management (composables, computed properties)
- API calls and data transformations
- Business logic and utility functions
- Edge cases and error handling
- Purely declarative templates (just the rendering result without logic)
- Third-party library internals
Frontend Testing
Testing Framework
Kolibri uses Jest as the test runner for frontend tests.Testing Library
Use Vue Testing Library for all new tests. Vue Test Utils is legacy and being phased out.
- New tests: Use Vue Testing Library
- Existing tests: vue-test-utils tests remain but should not be extended
Running Frontend Tests
Frontend Test Example
Frontend Testing Best Practices
File Organization
Test files are located in__tests__/ directories:
Naming Conventions
Test files should follow the naming convention:<Name of the file being tested>.spec.js
Use renderComponent Function
Define a renderComponent helper function to avoid repeating boilerplate:
Add Smoke Tests
Add a smoke test to every test suite that only renders the most basic example of a component. This ensures the component is not broken due to basic errors like missing imports or syntax errors.Use the screen Object
For querying DOM nodes, use the screen object provided by @testing-library/vue:
Prefer userEvent Over fireEvent
@testing-library/user-event provides methods that resemble user interactions more closely than fireEvent.
For example, userEvent.type triggers keyDown, keyPress, and keyUp events for each character, while fireEvent.change only triggers a single change event.
Use testing-library/jest-dom Matchers
testing-library/jest-dom provides custom matchers that make tests more declarative:
Backend Testing
Testing Framework
Kolibri uses pytest as the test runner for backend tests.- Django API tests: Extend
APITestCasefromrest_framework.test - Other Django tests: Extend
django.test.TestCase - Non-Django code: Use bare pytest-style function tests
Running Backend Tests
Backend Test Example
Backend Testing Best Practices
- Test through the API: Backend tests should call API endpoints through Django’s test client and assert on response data, not on internal method calls
- Use fixtures: Use pytest fixtures or Django’s
setUp()method to create test data - Test permissions: Ensure API endpoints have appropriate authentication and authorization
- Test edge cases: Empty querysets, invalid input, missing data, etc.
- Mock external services: Use mocks for external APIs, file system operations, etc.
Mocking
When to Mock
Mock only at hard boundaries:- Network calls
- Filesystem operations
- External services
- Time-dependent operations (when testing time-specific behavior)
Frontend Mocking
Usejest.fn() and jest.mock() for mocking:
Backend Mocking
Useunittest.mock or pytest-mock for mocking:
Coverage Requirements
Focus test coverage on:- Critical business logic
- Complex algorithms
- Error handling and edge cases
- Security-sensitive code
- Code that has had bugs in the past
- Public APIs and interfaces
Resources
Frontend Testing
- Vue Testing Library Documentation
- Jest Documentation
- Testing Library Queries
- User Event Documentation
- Common Mistakes with Testing Library