202 lines
7.1 KiB
Python
202 lines
7.1 KiB
Python
"""
|
|
Tests for Sentiment Analyzer
|
|
|
|
This module tests the VADER sentiment analyzer functionality.
|
|
"""
|
|
|
|
import pytest
|
|
from app.ml.sentiment_analyzer import (
|
|
analyze_sentiment,
|
|
analyze_sentiment_batch,
|
|
calculate_aggregated_metrics,
|
|
classify_sentiment,
|
|
test_analyzer_performance
|
|
)
|
|
|
|
|
|
class TestClassifySentiment:
|
|
"""Test sentiment classification based on compound score."""
|
|
|
|
def test_positive_classification(self):
|
|
"""Test classification of positive sentiment."""
|
|
assert classify_sentiment(0.5) == 'positive'
|
|
assert classify_sentiment(0.05) == 'positive'
|
|
assert classify_sentiment(1.0) == 'positive'
|
|
|
|
def test_negative_classification(self):
|
|
"""Test classification of negative sentiment."""
|
|
assert classify_sentiment(-0.5) == 'negative'
|
|
assert classify_sentiment(-0.05) == 'negative'
|
|
assert classify_sentiment(-1.0) == 'negative'
|
|
|
|
def test_neutral_classification(self):
|
|
"""Test classification of neutral sentiment."""
|
|
assert classify_sentiment(0.0) == 'neutral'
|
|
assert classify_sentiment(0.04) == 'neutral'
|
|
assert classify_sentiment(-0.04) == 'neutral'
|
|
|
|
|
|
class TestAnalyzeSentiment:
|
|
"""Test single text sentiment analysis."""
|
|
|
|
def test_positive_text(self):
|
|
"""Test analysis of positive text."""
|
|
text = "I love this game! It's absolutely amazing and wonderful!"
|
|
result = analyze_sentiment(text)
|
|
|
|
assert result['sentiment'] == 'positive'
|
|
assert result['compound'] > 0.05
|
|
assert 0 <= result['positive'] <= 1
|
|
assert 0 <= result['negative'] <= 1
|
|
assert 0 <= result['neutral'] <= 1
|
|
|
|
def test_negative_text(self):
|
|
"""Test analysis of negative text."""
|
|
text = "This is terrible! I hate it and it's the worst ever."
|
|
result = analyze_sentiment(text)
|
|
|
|
assert result['sentiment'] == 'negative'
|
|
assert result['compound'] < -0.05
|
|
assert 0 <= result['positive'] <= 1
|
|
assert 0 <= result['negative'] <= 1
|
|
assert 0 <= result['neutral'] <= 1
|
|
|
|
def test_neutral_text(self):
|
|
"""Test analysis of neutral text."""
|
|
text = "The game is okay. Nothing special but nothing bad."
|
|
result = analyze_sentiment(text)
|
|
|
|
assert result['sentiment'] in ['positive', 'negative', 'neutral']
|
|
assert 0 <= result['compound'] <= 1
|
|
assert 0 <= result['positive'] <= 1
|
|
assert 0 <= result['negative'] <= 1
|
|
assert 0 <= result['neutral'] <= 1
|
|
|
|
def test_empty_text_raises_error(self):
|
|
"""Test that empty text raises ValueError."""
|
|
with pytest.raises(ValueError):
|
|
analyze_sentiment("")
|
|
|
|
def test_invalid_input_raises_error(self):
|
|
"""Test that invalid input raises ValueError."""
|
|
with pytest.raises(ValueError):
|
|
analyze_sentiment(None)
|
|
|
|
with pytest.raises(ValueError):
|
|
analyze_sentiment(123)
|
|
|
|
|
|
class TestAnalyzeSentimentBatch:
|
|
"""Test batch sentiment analysis."""
|
|
|
|
def test_batch_analysis_multiple_texts(self):
|
|
"""Test analysis of multiple texts."""
|
|
texts = [
|
|
"I love this!",
|
|
"This is terrible!",
|
|
"It's okay."
|
|
]
|
|
results = analyze_sentiment_batch(texts)
|
|
|
|
assert len(results) == 3
|
|
assert all('compound' in r for r in results)
|
|
assert all('positive' in r for r in results)
|
|
assert all('negative' in r for r in results)
|
|
assert all('neutral' in r for r in results)
|
|
assert all('sentiment' in r for r in results)
|
|
|
|
def test_batch_analysis_empty_list(self):
|
|
"""Test batch analysis with empty list."""
|
|
results = analyze_sentiment_batch([])
|
|
assert results == []
|
|
|
|
def test_batch_analysis_with_errors(self):
|
|
"""Test batch analysis handles errors gracefully."""
|
|
texts = [
|
|
"This is good!",
|
|
"", # Invalid - should be handled gracefully
|
|
"This is great!"
|
|
]
|
|
results = analyze_sentiment_batch(texts)
|
|
|
|
assert len(results) == 3
|
|
# The invalid text should return neutral sentiment
|
|
assert results[1]['sentiment'] == 'neutral'
|
|
assert results[1]['compound'] == 0.0
|
|
|
|
|
|
class TestCalculateAggregatedMetrics:
|
|
"""Test calculation of aggregated sentiment metrics."""
|
|
|
|
def test_aggregated_metrics_empty_list(self):
|
|
"""Test aggregated metrics with empty list."""
|
|
metrics = calculate_aggregated_metrics([])
|
|
|
|
assert metrics['total_count'] == 0
|
|
assert metrics['positive_count'] == 0
|
|
assert metrics['negative_count'] == 0
|
|
assert metrics['neutral_count'] == 0
|
|
assert metrics['positive_ratio'] == 0.0
|
|
assert metrics['negative_ratio'] == 0.0
|
|
assert metrics['neutral_ratio'] == 0.0
|
|
assert metrics['average_compound'] == 0.0
|
|
|
|
def test_aggregated_metrics_all_positive(self):
|
|
"""Test aggregated metrics with all positive sentiments."""
|
|
sentiments = [
|
|
{'compound': 0.5, 'sentiment': 'positive'},
|
|
{'compound': 0.7, 'sentiment': 'positive'},
|
|
{'compound': 0.9, 'sentiment': 'positive'}
|
|
]
|
|
metrics = calculate_aggregated_metrics(sentiments)
|
|
|
|
assert metrics['total_count'] == 3
|
|
assert metrics['positive_count'] == 3
|
|
assert metrics['negative_count'] == 0
|
|
assert metrics['neutral_count'] == 0
|
|
assert metrics['positive_ratio'] == 1.0
|
|
assert metrics['average_compound'] == pytest.approx(0.7, rel=0.01)
|
|
|
|
def test_aggregated_metrics_mixed(self):
|
|
"""Test aggregated metrics with mixed sentiments."""
|
|
sentiments = [
|
|
{'compound': 0.5, 'sentiment': 'positive'},
|
|
{'compound': -0.5, 'sentiment': 'negative'},
|
|
{'compound': 0.0, 'sentiment': 'neutral'},
|
|
{'compound': 0.7, 'sentiment': 'positive'}
|
|
]
|
|
metrics = calculate_aggregated_metrics(sentiments)
|
|
|
|
assert metrics['total_count'] == 4
|
|
assert metrics['positive_count'] == 2
|
|
assert metrics['negative_count'] == 1
|
|
assert metrics['neutral_count'] == 1
|
|
assert metrics['positive_ratio'] == 0.5
|
|
assert metrics['negative_ratio'] == 0.25
|
|
assert metrics['neutral_ratio'] == 0.25
|
|
assert metrics['average_compound'] == pytest.approx(0.175, rel=0.01)
|
|
|
|
|
|
class TestAnalyzerPerformance:
|
|
"""Test performance of the sentiment analyzer."""
|
|
|
|
def test_performance_1000_tweets(self):
|
|
"""Test that 1000 tweets can be analyzed in less than 1 second."""
|
|
time_taken = test_analyzer_performance(1000)
|
|
|
|
assert time_taken < 1.0, f"Analysis took {time_taken:.4f}s, expected < 1.0s"
|
|
|
|
def test_performance_5000_tweets(self):
|
|
"""Test performance with 5000 tweets."""
|
|
time_taken = test_analyzer_performance(5000)
|
|
|
|
# Should still be fast, but we allow some scaling
|
|
assert time_taken < 5.0, f"Analysis took {time_taken:.4f}s, expected < 5.0s"
|
|
|
|
def test_performance_10000_tweets(self):
|
|
"""Test performance with 10000 tweets."""
|
|
time_taken = test_analyzer_performance(10000)
|
|
|
|
# Should still be very fast
|
|
assert time_taken < 10.0, f"Analysis took {time_taken:.4f}s, expected < 10.0s"
|