teachingAssistant / tests /unit /domain /interfaces /test_speech_recognition.py
Michael Hu
Create unit tests for domain layer
48f8a08
"""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"