# Recruitment Application Testing Guide This guide provides comprehensive information about testing the Recruitment Application (ATS) system. ## Test Structure The test suite is organized into several modules: ### 1. Basic Tests (`recruitment/tests.py`) - **BaseTestCase**: Common setup for all tests - **ModelTests**: Basic model functionality tests - **ViewTests**: Standard view tests - **FormTests**: Basic form validation tests - **IntegrationTests**: Simple integration scenarios ### 2. Advanced Tests (`recruitment/tests_advanced.py`) - **AdvancedModelTests**: Complex model scenarios and edge cases - **AdvancedViewTests**: Complex view logic with multiple filters and workflows - **AdvancedFormTests**: Complex form validation and dynamic fields - **AdvancedIntegrationTests**: End-to-end workflows and concurrent operations - **SecurityTests**: Security-focused testing ### 3. Configuration Files - **`pytest.ini`**: Pytest configuration with coverage settings - **`conftest.py`**: Pytest fixtures and common test setup ## Running Tests ### Basic Test Execution ```bash # Run all tests python manage.py test recruitment # Run specific test class python manage.py test recruitment.tests.AdvancedModelTests # Run with verbose output python manage.py test recruitment --verbosity=2 # Run tests with coverage python manage.py test recruitment --coverage ``` ### Using Pytest ```bash # Install pytest and required packages pip install pytest pytest-django pytest-cov # Run all tests pytest # Run specific test file pytest recruitment/tests.py # Run with coverage pytest --cov=recruitment --cov-report=html # Run with markers pytest -m unit # Run only unit tests pytest -m integration # Run only integration tests pytest -m "not slow" # Skip slow tests ``` ### Test Markers - `@pytest.mark.unit`: For unit tests - `@pytest.mark.integration`: For integration tests - `@pytest.mark.security`: For security tests - `@pytest.mark.api`: For API tests - `@pytest.mark.slow`: For performance-intensive tests ## Test Coverage The test suite aims for 80% code coverage. Coverage reports are generated in: - HTML: `htmlcov/index.html` - Terminal: Shows missing lines ### Improving Coverage 1. Add tests for untested branches 2. Test error conditions and edge cases 3. Use mocking for external dependencies ## Key Testing Areas ### 1. Model Testing - **JobPosting**: ID generation, validation, methods - **Candidate**: Stage transitions, relationships - **ZoomMeeting**: Time validation, status handling - **FormTemplate**: Template integrity, field ordering - **BulkInterviewTemplate**: Scheduling logic, slot generation ### 2. View Testing - **Job Management**: CRUD operations, search, filtering - **Candidate Management**: Stage updates, bulk operations - **Meeting Management**: Scheduling, API integration - **Form Handling**: Submission processing, validation ### 3. Form Testing - **JobPostingForm**: Complex validation, field dependencies - **CandidateForm**: File upload, validation - **BulkInterviewTemplateForm**: Dynamic fields, validation - **MeetingCommentForm**: Comment creation/editing ### 4. Integration Testing - **Complete Hiring Workflow**: Job → Application → Interview → Hire - **Data Integrity**: Cross-component data consistency - **API Integration**: Zoom API, LinkedIn integration - **Concurrent Operations**: Multi-threading scenarios ### 5. Security Testing - **Access Control**: Permission validation - **CSRF Protection**: Form security - **Input Validation**: SQL injection, XSS prevention - **Authentication**: User authorization ## Test Fixtures Common fixtures available in `conftest.py`: - **User Fixtures**: `user`, `staff_user`, `profile` - **Model Fixtures**: `job`, `candidate`, `zoom_meeting`, `form_template` - **Form Data Fixtures**: `job_form_data`, `candidate_form_data` - **Mock Fixtures**: `mock_zoom_api`, `mock_time_slots` - **Client Fixtures**: `client`, `authenticated_client`, `authenticated_staff_client` ## Writing New Tests ### Test Naming Convention - Use descriptive names: `test_user_can_create_job_posting` - Follow the pattern: `test_[subject]_[action]_[expected_result]` ### Best Practices 1. **Use Fixtures**: Leverage existing fixtures instead of creating test data 2. **Mock External Dependencies**: Use `@patch` for API calls 3. **Test Edge Cases**: Include invalid data, boundary conditions 4. **Maintain Independence**: Each test should be runnable independently 5. **Use Assertions**: Be specific about expected outcomes ### Example Test Structure ```python from django.test import TestCase from recruitment.models import JobPosting from recruitment.tests import BaseTestCase class JobPostingTests(BaseTestCase): def test_job_creation_minimal_data(self): """Test job creation with minimal required fields""" job = JobPosting.objects.create( title='Minimal Job', department='IT', job_type='FULL_TIME', workplace_type='REMOTE', created_by=self.user ) self.assertEqual(job.title, 'Minimal Job') self.assertIsNotNone(job.slug) def test_job_posting_validation_invalid_data(self): """Test that invalid data raises validation errors""" with self.assertRaises(ValueError): JobPosting.objects.create( title='', # Empty title department='IT', job_type='FULL_TIME', workplace_type='REMOTE', created_by=self.user ) ``` ## Testing External Integrations ### Zoom API Integration ```python @patch('recruitment.views.create_zoom_meeting') def test_meeting_creation(self, mock_zoom): """Test Zoom meeting creation with mocked API""" mock_zoom.return_value = { 'status': 'success', 'meeting_details': { 'meeting_id': '123456789', 'join_url': 'https://zoom.us/j/123456789' } } # Test meeting creation logic result = create_zoom_meeting('Test Meeting', start_time, duration) self.assertEqual(result['status'], 'success') mock_zoom.assert_called_once() ``` ### LinkedIn Integration ```python @patch('recruitment.views.LinkedinService') def test_linkedin_posting(self, mock_linkedin): """Test LinkedIn job posting with mocked service""" mock_service = mock_linkedin.return_value mock_service.create_job_post.return_value = { 'success': True, 'post_id': 'linkedin123', 'post_url': 'https://linkedin.com/jobs/view/linkedin123' } # Test LinkedIn posting logic result = mock_service.create_job_post(job) self.assertTrue(result['success']) ``` ## Performance Testing ### Running Performance Tests ```bash # Run slow tests only pytest -m slow # Profile test execution pytest --profile ``` ### Performance Considerations 1. Use `TransactionTestCase` for tests that require database commits 2. Mock external API calls to avoid network delays 3. Use `select_related` and `prefetch_related` in queries 4. Test with realistic data volumes ## Continuous Integration ### GitHub Actions Integration ```yaml name: Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest strategy: matrix: python-version: [3.9, 3.10, 3.11] steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install pytest pytest-django pytest-cov - name: Run tests run: | pytest --cov=recruitment --cov-report=xml - name: Upload coverage to Codecov uses: codecov/codecov-action@v1 ``` ## Troubleshooting Common Issues ### Database Issues ```python # Use TransactionTestCase for tests that modify database structure from django.test import TransactionTestCase class MyTests(TransactionTestCase): def test_database_modification(self): # This test will properly clean up the database pass ``` ### Mocking Issues ```python # Correct way to mock imports from unittest.mock import patch @patch('recruitment.views.zoom_api.ZoomClient') def test_zoom_integration(self, mock_zoom_client): mock_instance = mock_zoom_client.return_value mock_instance.create_meeting.return_value = {'success': True} # Test code ``` ### HTMX Testing ```python # Test HTMX responses def test_htmx_partial_update(self): response = self.client.get('/some-url/', HTTP_HX_REQUEST='true') self.assertEqual(response.status_code, 200) self.assertIn('partial-content', response.content) ``` ## Contributing to Tests ### Adding New Tests 1. Place tests in appropriate test modules 2. Use existing fixtures when possible 3. Add descriptive docstrings 4. Mark tests with appropriate markers 5. Ensure new tests maintain coverage requirements ### Test Review Checklist - [ ] Tests are properly isolated - [ ] Fixtures are used effectively - [ ] External dependencies are mocked - [ ] Edge cases are covered - [ ] Naming conventions are followed - [ ] Documentation is clear ## Resources - [Django Testing Documentation](https://docs.djangoproject.com/en/stable/topics/testing/) - [Pytest Documentation](https://docs.pytest.org/) - [Test-Driven Development](https://testdriven.io/blog/tdd-with-django-and-react/) - [Code Coverage Best Practices](https://pytest-cov.readthedocs.io/)