Multi-module test support framework for Embabel Agent applications providing integration testing, mock AI services, and test configuration utilities
Complete overview of the embabel-agent-test-common module providing fake AI services for testing.
The embabel-agent-test-common module provides fake implementations of AI services for testing without requiring API keys or making real API calls. The primary component is FakeEmbeddingModel, which generates random embeddings for testing embedding-related functionality.
Module Name: embabel-agent-test-common Package: com.embabel.common.test.ai Language: Kotlin (Java-compatible) Framework: Spring AI
Add to your Maven pom.xml:
<dependency>
<groupId>com.embabel.agent</groupId>
<artifactId>embabel-agent-test-common</artifactId>
<version>0.3.3</version>
<scope>test</scope>
</dependency>A fake implementation of Spring AI's EmbeddingModel interface that generates random embeddings.
Features:
EmbeddingModel interfaceUsage:
import com.embabel.common.test.ai.FakeEmbeddingModel
import org.springframework.ai.document.Document
// Create with default dimensions (1536)
val model = FakeEmbeddingModel()
// Or with custom dimensions
val model768 = FakeEmbeddingModel(dimensions = 768)
// Generate embeddings
val document = Document("test content")
val embedding = model.embed(document) // Returns random FloatArray of size 1536Java Usage:
import com.embabel.common.test.ai.FakeEmbeddingModel;
import org.springframework.ai.document.Document;
FakeEmbeddingModel model = new FakeEmbeddingModel(1536);
Document document = new Document("test content");
float[] embedding = model.embed(document);Embed individual documents:
val model = FakeEmbeddingModel(dimensions = 512)
val document = Document("Sample text")
val embedding = model.embed(document)
// Returns random 512-dimensional embeddingEmbed multiple texts at once:
val model = FakeEmbeddingModel()
val texts = listOf("text 1", "text 2", "text 3")
val embeddings = model.embed(texts)
// Returns list of 3 random 1536-dimensional embeddingsProcess Spring AI EmbeddingRequest:
val model = FakeEmbeddingModel(dimensions = 384)
val request = EmbeddingRequest(
listOf("query 1", "query 2"),
null
)
val response = model.call(request)
// Returns EmbeddingResponse with 2 random embeddingsTest storing and retrieving embeddings:
@Test
fun `test vector storage`() {
val embeddingModel = FakeEmbeddingModel()
val vectorStore = SimpleVectorStore(embeddingModel)
// Add documents with embeddings
vectorStore.add(listOf(
Document("doc1"),
Document("doc2")
))
// Retrieve (structure test, not semantic)
val docs = vectorStore.getAll()
assertEquals(2, docs.size)
}Test document processing pipelines:
@Test
fun `test embedding pipeline`() {
val model = FakeEmbeddingModel(dimensions = 768)
val pipeline = DocumentPipeline(model)
val documents = listOf(
Document("doc1"),
Document("doc2")
)
val processed = pipeline.process(documents)
assertEquals(2, processed.size)
processed.forEach { doc ->
assertNotNull(doc.embedding)
assertEquals(768, doc.embedding!!.size)
}
}Test search engine structure (not semantic quality):
@Test
fun `test search engine structure`() {
val embeddingModel = FakeEmbeddingModel()
val searchEngine = SemanticSearchEngine(embeddingModel)
searchEngine.indexDocuments(listOf("doc1", "doc2", "doc3"))
val results = searchEngine.search("query", topK = 2)
assertEquals(2, results.size)
results.forEach { result ->
assertNotNull(result.document)
assertTrue(result.score >= 0.0)
}
}Test services that use embeddings:
class EmbeddingService(private val embeddingModel: EmbeddingModel) {
fun embedText(text: String): FloatArray {
return embeddingModel.embed(Document(text))
}
}
@Test
fun `test embedding service`() {
val fakeModel = FakeEmbeddingModel(dimensions = 512)
val service = EmbeddingService(fakeModel)
val embedding = service.embedText("test")
assertEquals(512, embedding.size)
}The module supports any dimension size. Common choices:
| Model | Dimensions | Use Case |
|---|---|---|
| OpenAI ada-002 | 1536 | Default, general purpose |
| BERT base | 768 | NLP tasks |
| Sentence transformers | 384 | Efficient embeddings |
| Custom | Any | Match your specific model |
Example:
@Test
fun `test with different dimensions`() {
val model384 = FakeEmbeddingModel(dimensions = 384)
val model768 = FakeEmbeddingModel(dimensions = 768)
val model1536 = FakeEmbeddingModel(dimensions = 1536)
val text = "sample text"
val doc = Document(text)
assertEquals(384, model384.embed(doc).size)
assertEquals(768, model768.embed(doc).size)
assertEquals(1536, model1536.embed(doc).size)
}@SpringBootTest
class EmbeddingIntegrationTest {
@TestConfiguration
class TestConfig {
@Bean
fun embeddingModel(): EmbeddingModel {
return FakeEmbeddingModel(dimensions = 768)
}
}
@Autowired
private lateinit var embeddingModel: EmbeddingModel
@Test
fun `test with Spring bean`() {
val embedding = embeddingModel.embed(Document("test"))
assertEquals(768, embedding.size)
}
}For complete AI test setup, use embabel-agent-test-internal:
@SpringBootTest
@Import(FakeAiConfiguration::class)
class EmbeddingServiceTest {
@Autowired
private lateinit var embeddingService: EmbeddingService
@Test
fun `test embedding service`() {
val embedding = embeddingService.embed("test")
assertEquals(1536, embedding.size)
}
}This module depends on:
All dependencies are transitive.
Use this module when:
✓ Testing embedding storage and retrieval ✓ Testing vector database integrations ✓ Testing document processing pipelines ✓ Testing code that uses embeddings without needing real API ✓ Testing with different embedding dimensions ✓ Need fast tests without API calls
Don't use this module when:
✗ Testing semantic similarity quality (embeddings are random) ✗ Testing actual embedding model behavior ✗ Need semantically meaningful results ✗ Testing real-world search relevance ✗ Production use
DO Test:
DON'T Test:
Fake embeddings are extremely fast:
@Test
fun `test large batch performance`() {
val model = FakeEmbeddingModel()
val startTime = System.currentTimeMillis()
// Embed 10,000 documents instantly
val texts = (1..10000).map { "Document $it" }
val embeddings = model.embed(texts)
val duration = System.currentTimeMillis() - startTime
assertEquals(10000, embeddings.size)
println("Embedded 10k documents in ${duration}ms")
}| Feature | embabel-agent-test-common | embabel-agent-test | embabel-agent-test-internal |
|---|---|---|---|
| Fake embeddings | ✓ Yes | ✗ No | ✗ No (uses this module) |
| LLM stubbing | ✗ No | ✓ Yes | ✗ No |
| Spring config | ✗ No | ✗ No | ✓ Yes |
| Language | Kotlin | Java | Kotlin |
| Framework | Spring AI | Mockito | Spring Boot Test |
See also:
import com.embabel.common.test.ai.FakeEmbeddingModel
import org.springframework.ai.document.Document
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class DocumentEmbeddingTest {
@Test
fun `test document embedding pipeline`() {
// Create fake model
val embeddingModel = FakeEmbeddingModel(dimensions = 768)
// Create processing service
val processor = DocumentProcessor(embeddingModel)
// Process documents
val documents = listOf(
Document("First document about AI"),
Document("Second document about ML"),
Document("Third document about testing")
)
val processedDocs = processor.processDocuments(documents)
// Assert structure
assertEquals(3, processedDocs.size)
processedDocs.forEach { doc ->
assertNotNull(doc.embedding)
assertEquals(768, doc.embedding!!.size)
assertTrue(doc.embedding!!.all { it.isFinite() })
}
}
@Test
fun `test vector store integration`() {
val embeddingModel = FakeEmbeddingModel()
val vectorStore = InMemoryVectorStore(embeddingModel)
// Add documents
val doc1 = Document("content1")
val doc2 = Document("content2")
vectorStore.add(listOf(doc1, doc2))
// Verify storage
val stored = vectorStore.count()
assertEquals(2, stored)
// Test retrieval
val retrieved = vectorStore.getById(doc1.id)
assertNotNull(retrieved)
assertNotNull(retrieved.embedding)
assertEquals(1536, retrieved.embedding!!.size)
}
@Test
fun `test batch embedding performance`() {
val model = FakeEmbeddingModel()
// Generate large batch
val largeBatch = (1..1000).map { "Document $it" }
val start = System.currentTimeMillis()
val embeddings = model.embed(largeBatch)
val duration = System.currentTimeMillis() - start
assertEquals(1000, embeddings.size)
assertTrue(duration < 1000, "Should be fast: ${duration}ms")
}
@Test
fun `test different dimension sizes`() {
val models = mapOf(
384 to FakeEmbeddingModel(dimensions = 384),
768 to FakeEmbeddingModel(dimensions = 768),
1536 to FakeEmbeddingModel(dimensions = 1536)
)
models.forEach { (expectedDim, model) ->
val embedding = model.embed(Document("test"))
assertEquals(expectedDim, embedding.size,
"Model should produce ${expectedDim}-dimensional embeddings")
}
}
}