This guide explains the testing requirements for contributing code to Gofannon.
Every PR must include:
Before submitting your PR, verify:
Only exclude from coverage:
if TYPE_CHECKING:)@abstractmethod)if __name__ == "__main__":)pragma: no cover)Never exclude:
# Backend (Python) tests/unit/test_<module_name>.py tests/integration/test_<feature_name>.py # Frontend (JavaScript) src/components/ComponentName.test.jsx src/utils/utilityName.test.js
# Backend class TestFeatureName: """Test suite for FeatureName.""" def test_feature_does_something_when_condition(self): """Test that feature does X when Y happens.""" # Arrange # Act # Assert
// Frontend describe('ComponentName', () => { describe('when prop X is true', () => { it('renders element Y', () => { // Arrange, Act, Assert }); }); });
Required Tests:
Example:
# tests/unit/test_routes.py def test_create_agent_validates_input(mock_db): with pytest.raises(ValidationError): create_agent(CreateAgentRequest(name="")) # Empty name def test_create_agent_saves_to_database(mock_db): agent = create_agent(CreateAgentRequest(name="Test")) mock_db.save.assert_called_once() # tests/integration/test_agent_endpoints.py def test_create_agent_endpoint(client): response = client.post("/agents", json={"name": "Test"}) assert response.status_code == 201 assert response.json()["name"] == "Test"
Required Tests:
Example:
// ActionCard.test.jsx describe('ActionCard', () => { it('renders with required props', () => { render(<ActionCard {...requiredProps} />); expect(screen.getByText('Title')).toBeInTheDocument(); }); it('calls onClick when clicked', async () => { const onClick = vi.fn(); render(<ActionCard {...requiredProps} onClick={onClick} />); await userEvent.click(screen.getByRole('button')); expect(onClick).toHaveBeenCalled(); }); it('shows error message when prop is invalid', () => { render(<ActionCard {...requiredProps} title="" />); expect(screen.getByText('Title is required')).toBeInTheDocument(); }); });
# 1. Run all unit tests cd webapp pnpm test:unit # 2. Check coverage pnpm test:coverage # 3. Run integration tests if you changed API/services pnpm test:integration # 4. Run lint cd packages/webui pnpm lint # 5. Verify everything passes cd ../.. pnpm test
If CI fails:
Reviewers will check:
Title: Add user allowance reset endpoint Tests Added: - test_reset_allowance_sets_to_monthly_limit (unit) - test_reset_allowance_clears_usage_history (unit) - test_reset_allowance_endpoint_returns_updated_user (integration) - test_reset_allowance_requires_authentication (integration) Coverage: 98% on modified files
Title: Fix user creation with missing email Tests Added: - test_create_user_without_email_uses_default (unit) - test_create_user_with_null_email_raises_error (unit) Before: Bug allowed null emails After: Bug fixed, tests verify correct behavior Coverage: 100% on user_service.py
pnpm test:coverage to see uncovered lines# Bad - only tests success def test_create_user(): user = create_user("test@example.com") assert user.email == "test@example.com" # Good - tests error cases too def test_create_user_with_invalid_email(): with pytest.raises(ValidationError): create_user("invalid-email") def test_create_user_with_duplicate_email(): create_user("test@example.com") with pytest.raises(DuplicateEmailError): create_user("test@example.com")
// Bad - tests implementation detail it('calls setState with correct value', () => { const setState = vi.spyOn(component, 'setState'); component.handleClick(); expect(setState).toHaveBeenCalledWith({ clicked: true }); }); // Good - tests visible behavior it('shows success message after clicking', async () => { render(<Component />); await userEvent.click(screen.getByRole('button')); expect(screen.getByText('Success!')).toBeInTheDocument(); });
# Bad - shared state user = User(_id="test-123") def test_update_email(): user.email = "new@example.com" # Modifies shared object! def test_update_name(): user.name = "New Name" # Depends on previous test! # Good - isolated tests def test_update_email(): user = User(_id="test-123") user.email = "new@example.com" def test_update_name(): user = User(_id="test-123") user.name = "New Name"
# Bad - only unit tests for new endpoint def test_create_agent_saves_to_db(mock_db): create_agent(data, mock_db) mock_db.save.assert_called() # Good - also has integration test def test_create_agent_endpoint_e2e(client): response = client.post("/agents", json=valid_data) assert response.status_code == 201 # Verify it's actually in the database agent = client.get(f"/agents/{response.json()['id']}") assert agent.json()["name"] == valid_data["name"]
Your PR will be approved when: