Módulo: Gestión de Eventos & Alertas · Django 4.2 + DRF + pytest-django + factory_boy
Definir el comportamiento esperado antes de escribir código. El test falla porque la funcionalidad no existe.
Escribir el mínimo código necesario para que el test pase. Sin sobreingeniería.
Limpiar, simplificar y optimizar manteniendo todos los tests en verde.
# Crear lote de 10 eventos de conversión EventFactory.create_batch(10, workspace=workspace, event_type="conversion" ) # Factory con override de propiedades AlertFactory( threshold=50, notify_slack=True, slack_webhook_url="https://hooks.slack.com/..." )
| Test | Módulo | Tipo | Estado | ms |
|---|---|---|---|---|
| test_workspace_creation | TestWorkspaceModel | Model | PASS | 8ms |
| test_workspace_slug_unique | TestWorkspaceModel | Model | PASS | 12ms |
| test_workspace_rotate_api_key | TestWorkspaceModel | Model | PASS | 6ms |
| test_event_creation | TestEventModel | Model | PASS | 9ms |
| test_event_properties_json | TestEventModel | Model | PASS | 11ms |
| test_alert_should_trigger_true | TestAlertModel | Model | PASS | 7ms |
| test_alert_with_slack_requires_url | TestAlertModel | Model | PASS | 5ms |
| test_serialize_event | TestEventSerializer | Serializer | PASS | 14ms |
| test_deserialize_invalid_event_type | TestEventSerializer | Serializer | PASS | 8ms |
| test_slack_url_required_when_notify_slack | TestAlertSerializer | Serializer | PASS | 6ms |
| test_ingest_event_with_api_key | TestEventAPI | API | PASS | 22ms |
| test_ingest_event_without_api_key_returns_401 | TestEventAPI | API | PASS | 15ms |
| test_list_events_filtered_by_type | TestEventAPI | API | PASS | 18ms |
| test_create_alert | TestAlertAPI | API | PASS | 20ms |
| test_list_alerts_only_own_workspace | TestAlertAPI | API | PASS | 17ms |
| test_slack_notification_sent_on_trigger | TestSlackService | Mock | PASS | 4ms |
| test_slack_failure_does_not_raise | TestSlackService | Mock | PASS | 3ms |
| test_full_alert_pipeline | TestAlertPipeline | Integration | PASS | 45ms |
| test_alert_not_triggered_above_threshold | TestAlertPipeline | Integration | PASS | 38ms |
| + 9 tests adicionales (modelos y serializers) | ||||
@pytest.fixture def workspace_api_client(api_client, workspace): """Cliente con X-API-Key.""" api_client.credentials( HTTP_X_API_KEY=workspace.api_key ) return api_client @pytest.fixture def mock_slack(): """Mock del webhook Slack.""" with patch( "apps.events.services.slack" ".send_notification" ) as mock: mock.return_value = MagicMock( status_code=200 ) yield mock
# 🔴 RED — test falla primero def test_alert_should_trigger_true( self, db, alert ): alert.threshold = 100 result = alert.should_trigger( actual_value=50 ) assert result is True # 🟢 GREEN — implementar método def should_trigger(self, actual_value): if self.condition == "count_below": return actual_value < self.threshold return actual_value > self.threshold
def test_ingest_event_with_api_key( self, workspace_api_client, workspace, db ): payload = { "event_type": "pageview", "session_id": str(uuid4()), "user_id": "usr_001", "properties": { "page": "/pricing" }, } response = workspace_api_client.post( "/api/events/", payload ) assert response.status_code == 201
[pytest] DJANGO_SETTINGS_MODULE = datapulse.settings.test testpaths = tests python_files = test_*.py addopts = --reuse-db # reutilizar BD entre runs --nomigrations # deshabilitar migraciones --cov=apps --cov-report=html --cov-report=term-missing --strict-markers markers = slow: tests lentos de integración integration: flujo completo unit: tests unitarios rápidos
from .base import * DATABASES = {'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:', # ← RAM, no disco }} class DisableMigrations: def __contains__(self, item): return True def __getitem__(self, item): return None MIGRATION_MODULES = DisableMigrations() # Hash rápido para tests PASSWORD_HASHERS = [ 'django.contrib.auth.hashers' '.MD5PasswordHasher' ] CELERY_TASK_ALWAYS_EAGER = True