Documentation Index
Fetch the complete documentation index at: https://mintlify.com/egeuysall/ryva-archive/llms.txt
Use this file to discover all available pages before exploring further.
Ryva maintains a comprehensive testing strategy with an 80% coverage requirement enforced in CI/CD.
Testing Stack
Frontend (Web)
- Vitest - Unit and integration testing
- React Testing Library - Component testing
- Playwright - End-to-end testing
- @vitest/coverage-v8 - Coverage reporting
Backend (API)
- Go testing - Built-in testing framework
- testify - Assertion and mocking library
- httptest - HTTP handler testing
- Coverage tools - Go coverage reporting
Running Tests
Quick Commands
Frontend Test Commands
Navigate to Web Directory
Run Tests
Backend Test Commands
Navigate to API Directory
Run Tests
Coverage Requirements
Mandatory Coverage Threshold: 80%All pull requests must maintain at least 80% test coverage. CI/CD will fail if coverage drops below this threshold.
Viewing Coverage Reports
cd apps/web
pnpm test:coverage
# Opens coverage report in browser
Writing Tests
Frontend Testing Guidelines
Component Tests
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { UserProfile } from './UserProfile';
describe('UserProfile', () => {
it('renders user information correctly', () => {
const user = {
name: 'John Doe',
email: 'john@example.com',
};
render(<UserProfile user={user} />);
expect(screen.getByText('John Doe')).toBeInTheDocument();
expect(screen.getByText('john@example.com')).toBeInTheDocument();
});
it('handles missing user gracefully', () => {
render(<UserProfile user={null} />);
expect(screen.getByText('No user found')).toBeInTheDocument();
});
});
Hook Tests
import { renderHook, waitFor } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { useUserData } from './useUserData';
describe('useUserData', () => {
it('fetches user data successfully', async () => {
const { result } = renderHook(() => useUserData('user-123'));
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
});
expect(result.current.data).toBeDefined();
expect(result.current.error).toBeNull();
});
});
E2E Tests (Playwright)
import { test, expect } from '@playwright/test';
test.describe('Authentication Flow', () => {
test('user can log in successfully', async ({ page }) => {
await page.goto('http://localhost:3000/login');
await page.fill('input[name="email"]', 'test@example.com');
await page.fill('input[name="password"]', 'password123');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('http://localhost:3000/dashboard');
await expect(page.locator('h1')).toContainText('Dashboard');
});
test('shows error for invalid credentials', async ({ page }) => {
await page.goto('http://localhost:3000/login');
await page.fill('input[name="email"]', 'wrong@example.com');
await page.fill('input[name="password"]', 'wrongpassword');
await page.click('button[type="submit"]');
await expect(page.locator('.error-message')).toBeVisible();
});
});
Backend Testing Guidelines
Unit Tests
package service
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCalculateTotal(t *testing.T) {
tests := []struct {
name string
items []Item
expected float64
}{
{
name: "empty cart",
items: []Item{},
expected: 0.0,
},
{
name: "single item",
items: []Item{
{Price: 10.00, Quantity: 2},
},
expected: 20.00,
},
{
name: "multiple items",
items: []Item{
{Price: 10.00, Quantity: 2},
{Price: 5.00, Quantity: 3},
},
expected: 35.00,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := CalculateTotal(tt.items)
assert.Equal(t, tt.expected, result)
})
}
}
HTTP Handler Tests
package handler
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
func TestCreateUserHandler(t *testing.T) {
// Setup
reqBody := map[string]string{
"name": "John Doe",
"email": "john@example.com",
}
body, _ := json.Marshal(reqBody)
req := httptest.NewRequest(http.MethodPost, "/users", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
// Execute
handler := NewUserHandler(mockUserService)
handler.CreateUser(w, req)
// Assert
assert.Equal(t, http.StatusCreated, w.Code)
var response map[string]interface{}
json.Unmarshal(w.Body.Bytes(), &response)
assert.Equal(t, "John Doe", response["name"])
}
Integration Tests
package integration
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestUserServiceIntegration(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test")
}
// Setup test database
db := setupTestDB(t)
defer db.Close()
ctx := context.Background()
userService := NewUserService(db)
// Create user
user, err := userService.CreateUser(ctx, &CreateUserRequest{
Name: "Test User",
Email: "test@example.com",
})
require.NoError(t, err)
assert.NotEmpty(t, user.ID)
// Fetch user
fetchedUser, err := userService.GetUser(ctx, user.ID)
require.NoError(t, err)
assert.Equal(t, user.Email, fetchedUser.Email)
// Update user
err = userService.UpdateUser(ctx, user.ID, &UpdateUserRequest{
Name: "Updated Name",
})
require.NoError(t, err)
// Verify update
updatedUser, err := userService.GetUser(ctx, user.ID)
require.NoError(t, err)
assert.Equal(t, "Updated Name", updatedUser.Name)
}
Testing Best Practices
General Guidelines
- Write tests first - Consider TDD approach
- Test behavior, not implementation - Focus on what, not how
- Keep tests independent - Each test should run in isolation
- Use descriptive names - Test names should explain what they test
- Follow AAA pattern - Arrange, Act, Assert
- Mock external dependencies - Use mocks for APIs, databases, etc.
- Test edge cases - Include boundary conditions and error cases
- Maintain tests - Update tests when code changes
Frontend Best Practices
- Use Testing Library queries -
getByRole, getByLabelText, etc.
- Avoid implementation details - Don’t test CSS classes or component internals
- Test user interactions - Simulate real user behavior
- Use accessible queries - Encourages accessible components
- Wait for async updates - Use
waitFor for async operations
- Clean up after tests - Use cleanup utilities
Backend Best Practices
- Use table-driven tests - Test multiple scenarios efficiently
- Always use context.Context - For database operations and cancellation
- Test error paths - Ensure errors are handled correctly
- Use testify assertions - More readable than manual checks
- Mock interfaces, not structs - Keep mocks flexible
- Test concurrent scenarios - Use goroutines to test race conditions
CI/CD Integration
Automated Testing in CI
All tests run automatically on:
- Every pull request
- Every push to develop/master
- Before deployment
CI Test Workflow
Linting
Code style checks run first
Unit Tests
Fast unit tests run in parallel
Integration Tests
Integration tests verify module interactions
E2E Tests
Full application tests run last
Coverage Check
Coverage must be ≥80% or CI fails
If any test fails or coverage drops below 80%, the PR cannot be merged.
Troubleshooting
Common Issues
Tests Fail Locally But Pass in CI
- Ensure
.env files are configured correctly
- Check for system-specific dependencies
- Verify database state is clean between tests
Flaky Tests
- Add proper waits for async operations
- Avoid hardcoded timeouts
- Ensure tests don’t depend on timing
- Check for race conditions
Low Coverage
- Identify untested code with coverage reports
- Add tests for edge cases
- Test error handling paths
- Don’t test external libraries
Run make test-coverage (API) or pnpm test:coverage (Web) to see exactly which lines need test coverage.
Continuous Improvement
- Review test coverage regularly
- Refactor tests when code changes
- Remove obsolete tests
- Keep test suite fast (under 5 minutes)
- Document complex test scenarios
- Share testing knowledge with team