teachingAssistant / tests /unit /domain /interfaces /test_speech_recognition.py
Michael Hu
Create unit tests for domain layer
48f8a08
raw
history blame
9.65 kB
"""Unit tests for ISpeechRecognitionService interface contract."""
import pytest
from abc import ABC
from unittest.mock import Mock
from src.domain.interfaces.speech_recognition import ISpeechRecognitionService
from src.domain.models.audio_content import AudioContent
from src.domain.models.text_content import TextContent
class TestISpeechRecognitionService:
"""Test cases for ISpeechRecognitionService interface contract."""
def test_interface_is_abstract(self):
"""Test that ISpeechRecognitionService is an abstract base class."""
assert issubclass(ISpeechRecognitionService, ABC)
# Should not be able to instantiate directly
with pytest.raises(TypeError):
ISpeechRecognitionService() # type: ignore
def test_interface_has_required_method(self):
"""Test that interface defines the required abstract method."""
# Check that the method exists and is abstract
assert hasattr(ISpeechRecognitionService, 'transcribe')
assert getattr(ISpeechRecognitionService.transcribe, '__isabstractmethod__', False)
def test_method_signature(self):
"""Test that the method has the correct signature."""
import inspect
method = ISpeechRecognitionService.transcribe
signature = inspect.signature(method)
# Check parameter names
params = list(signature.parameters.keys())
expected_params = ['self', 'audio', 'model']
assert params == expected_params
# Check return annotation
assert signature.return_annotation == "'TextContent'"
def test_concrete_implementation_must_implement_method(self):
"""Test that concrete implementations must implement the abstract method."""
class IncompleteImplementation(ISpeechRecognitionService):
pass
# Should not be able to instantiate without implementing abstract method
with pytest.raises(TypeError, match="Can't instantiate abstract class"):
IncompleteImplementation() # type: ignore
def test_concrete_implementation_with_method(self):
"""Test that concrete implementation with method can be instantiated."""
class ConcreteImplementation(ISpeechRecognitionService):
def transcribe(self, audio, model):
return TextContent(text="transcribed text", language="en")
# Should be able to instantiate
implementation = ConcreteImplementation()
assert isinstance(implementation, ISpeechRecognitionService)
def test_method_contract_with_mock(self):
"""Test the method contract using a mock implementation."""
class MockImplementation(ISpeechRecognitionService):
def __init__(self):
self.mock_method = Mock()
def transcribe(self, audio, model):
return self.mock_method(audio, model)
# Create test data
audio = AudioContent(
data=b"test_audio",
format="wav",
sample_rate=22050,
duration=5.0
)
model = "whisper-base"
expected_result = TextContent(text="Hello world", language="en")
# Setup mock
implementation = MockImplementation()
implementation.mock_method.return_value = expected_result
# Call method
result = implementation.transcribe(audio=audio, model=model)
# Verify call and result
implementation.mock_method.assert_called_once_with(audio, model)
assert result == expected_result
def test_interface_docstring_requirements(self):
"""Test that the interface method has proper documentation."""
method = ISpeechRecognitionService.transcribe
assert method.__doc__ is not None
docstring = method.__doc__
# Check that docstring contains key information
assert "Transcribe audio content to text" in docstring
assert "Args:" in docstring
assert "Returns:" in docstring
assert "Raises:" in docstring
assert "SpeechRecognitionException" in docstring
def test_interface_type_hints(self):
"""Test that the interface uses proper type hints."""
method = ISpeechRecognitionService.transcribe
annotations = getattr(method, '__annotations__', {})
assert 'audio' in annotations
assert 'model' in annotations
assert 'return' in annotations
# Check that type annotations are correct
assert annotations['audio'] == "'AudioContent'"
assert annotations['model'] == str
assert annotations['return'] == "'TextContent'"
def test_multiple_implementations_possible(self):
"""Test that multiple implementations of the interface are possible."""
class WhisperImplementation(ISpeechRecognitionService):
def transcribe(self, audio, model):
return TextContent(text="whisper transcription", language="en")
class ParakeetImplementation(ISpeechRecognitionService):
def transcribe(self, audio, model):
return TextContent(text="parakeet transcription", language="en")
whisper = WhisperImplementation()
parakeet = ParakeetImplementation()
assert isinstance(whisper, ISpeechRecognitionService)
assert isinstance(parakeet, ISpeechRecognitionService)
assert type(whisper) != type(parakeet)
def test_interface_method_can_be_called_polymorphically(self):
"""Test that interface methods can be called polymorphically."""
class TestImplementation(ISpeechRecognitionService):
def __init__(self, transcription_text):
self.transcription_text = transcription_text
def transcribe(self, audio, model):
return TextContent(text=self.transcription_text, language="en")
# Create different implementations
implementations = [
TestImplementation("first transcription"),
TestImplementation("second transcription")
]
# Test polymorphic usage
audio = AudioContent(data=b"test", format="wav", sample_rate=22050, duration=1.0)
model = "test-model"
results = []
for impl in implementations:
# Can call the same method on different implementations
result = impl.transcribe(audio, model)
results.append(result.text)
assert results == ["first transcription", "second transcription"]
def test_interface_inheritance_chain(self):
"""Test the inheritance chain of the interface."""
# Check that it inherits from ABC
assert ABC in ISpeechRecognitionService.__mro__
# Check that it's at the right position in MRO
mro = ISpeechRecognitionService.__mro__
assert mro[0] == ISpeechRecognitionService
assert ABC in mro
def test_method_parameter_validation_in_implementation(self):
"""Test that implementations can validate parameters."""
class ValidatingImplementation(ISpeechRecognitionService):
def transcribe(self, audio, model):
if not isinstance(audio, AudioContent):
raise TypeError("audio must be AudioContent")
if not isinstance(model, str):
raise TypeError("model must be string")
if not model.strip():
raise ValueError("model cannot be empty")
return TextContent(text="validated transcription", language="en")
impl = ValidatingImplementation()
# Valid call should work
audio = AudioContent(data=b"test", format="wav", sample_rate=22050, duration=1.0)
result = impl.transcribe(audio, "whisper-base")
assert result.text == "validated transcription"
# Invalid calls should raise appropriate errors
with pytest.raises(TypeError, match="audio must be AudioContent"):
impl.transcribe("not audio", "whisper-base") # type: ignore
with pytest.raises(TypeError, match="model must be string"):
impl.transcribe(audio, 123) # type: ignore
with pytest.raises(ValueError, match="model cannot be empty"):
impl.transcribe(audio, "")
def test_implementation_can_handle_different_models(self):
"""Test that implementations can handle different model types."""
class MultiModelImplementation(ISpeechRecognitionService):
def transcribe(self, audio, model):
model_responses = {
"whisper-tiny": "tiny transcription",
"whisper-base": "base transcription",
"whisper-large": "large transcription",
"parakeet": "parakeet transcription"
}
transcription = model_responses.get(model, "unknown model transcription")
return TextContent(text=transcription, language="en")
impl = MultiModelImplementation()
audio = AudioContent(data=b"test", format="wav", sample_rate=22050, duration=1.0)
# Test different models
models_and_expected = [
("whisper-tiny", "tiny transcription"),
("whisper-base", "base transcription"),
("whisper-large", "large transcription"),
("parakeet", "parakeet transcription"),
("unknown-model", "unknown model transcription")
]
for model, expected_text in models_and_expected:
result = impl.transcribe(audio, model)
assert result.text == expected_text
assert result.language == "en"