Testing
Strategies for testing LLM-powered applications with zyn.
Mock Providers
Fixed Response
func TestClassification(t *testing.T) {
provider := zyn.NewMockProviderWithResponse(`{
"primary": "spam",
"secondary": "",
"confidence": 0.95,
"reasoning": ["Contains promotional language"]
}`)
classifier, _ := zyn.Classification(
"Classify email type",
[]string{"spam", "urgent", "personal"},
provider,
)
session := zyn.NewSession()
result, err := classifier.Fire(context.Background(), session, "Buy now!")
assert.NoError(t, err)
assert.Equal(t, "spam", result)
}
Dynamic Response
func TestDynamicResponse(t *testing.T) {
provider := zyn.NewMockProviderWithCallback(func(prompt string, temp float32) (string, error) {
if strings.Contains(prompt, "valid") {
return `{"decision": true, "confidence": 0.9, "reasoning": ["ok"]}`, nil
}
return `{"decision": false, "confidence": 0.9, "reasoning": ["invalid"]}`, nil
})
validator, _ := zyn.Binary("Is this valid?", provider)
session := zyn.NewSession()
result, _ := validator.Fire(context.Background(), session, "valid input")
assert.True(t, result)
}
Error Simulation
func TestErrorHandling(t *testing.T) {
provider := zyn.NewMockProviderWithError("rate limit exceeded")
synapse, _ := zyn.Binary("question", provider)
session := zyn.NewSession()
_, err := synapse.Fire(context.Background(), session, "input")
assert.Error(t, err)
assert.Contains(t, err.Error(), "rate limit")
}
Testing Package
The testing package provides advanced test utilities:
import zynt "github.com/zoobz-io/zyn/testing"
Response Builder
Build valid responses easily:
// Binary response
response := zynt.NewResponseBuilder().
WithDecision(true).
WithConfidence(0.95).
WithReasoning("Valid format", "Contains required fields").
Build()
// Classification response
response := zynt.NewResponseBuilder().
WithPrimary("urgent").
WithSecondary("").
WithConfidence(0.87).
WithReasoning("Time-sensitive language detected").
Build()
// Ranking response
response := zynt.NewResponseBuilder().
WithRanked("first", "second", "third").
WithConfidence(0.9).
WithReasoning("Ordered by relevance").
Build()
Sequenced Provider
Return different responses in sequence:
provider := zynt.NewSequencedProvider(
zynt.NewResponseBuilder().WithDecision(true).WithConfidence(0.9).WithReasoning("first").Build(),
zynt.NewResponseBuilder().WithDecision(false).WithConfidence(0.8).WithReasoning("second").Build(),
)
synapse, _ := zyn.Binary("question", provider)
session := zyn.NewSession()
result1, _ := synapse.Fire(ctx, session, "input1") // true
result2, _ := synapse.Fire(ctx, session, "input2") // false
Failing Provider
Test retry behavior:
// Fails twice, then succeeds
provider := zynt.NewFailingProvider(2).
WithSuccessResponse(zynt.NewResponseBuilder().
WithDecision(true).
WithConfidence(0.9).
WithReasoning("recovered").
Build())
synapse, _ := zyn.Binary("question", provider, zyn.WithRetry(3))
session := zyn.NewSession()
result, err := synapse.Fire(ctx, session, "input")
assert.NoError(t, err) // Succeeds on third attempt
assert.True(t, result)
assert.Equal(t, 3, provider.CallCount())
Call Recorder
Inspect provider calls:
inner := zynt.NewSequencedProvider(`{"decision": true, "confidence": 0.9, "reasoning": ["ok"]}`)
recorder := zynt.NewCallRecorder(inner)
synapse, _ := zyn.Binary("question", recorder)
synapse.Fire(ctx, session, "test input")
calls := recorder.Calls()
assert.Len(t, calls, 1)
assert.Equal(t, "test input", calls[0].Messages[0].Content)
lastCall := recorder.LastCall()
assert.NotNil(t, lastCall)
Latency Provider
Test timeout behavior:
inner := zynt.NewSequencedProvider(`{"decision": true, "confidence": 0.9, "reasoning": ["ok"]}`)
slowProvider := zynt.NewLatencyProvider(inner, 500*time.Millisecond)
synapse, _ := zyn.Binary("question", slowProvider, zyn.WithTimeout(100*time.Millisecond))
session := zyn.NewSession()
_, err := synapse.Fire(ctx, session, "input")
assert.Error(t, err) // Timeout before provider responds
Testing Patterns
Session State
func TestSessionUpdates(t *testing.T) {
provider := zyn.NewMockProviderWithResponse(`{...}`)
synapse, _ := zyn.Binary("question", provider)
session := zyn.NewSession()
assert.Equal(t, 0, session.Len())
synapse.Fire(ctx, session, "input")
assert.Equal(t, 2, session.Len()) // user + assistant
assert.Equal(t, zyn.RoleUser, session.Messages()[0].Role)
assert.Equal(t, zyn.RoleAssistant, session.Messages()[1].Role)
}
Transactional Behavior
func TestSessionUnchangedOnError(t *testing.T) {
provider := zyn.NewMockProviderWithError("failure")
synapse, _ := zyn.Binary("question", provider, zyn.WithRetry(2))
session := zyn.NewSession()
initialLen := session.Len()
_, err := synapse.Fire(ctx, session, "input")
assert.Error(t, err)
assert.Equal(t, initialLen, session.Len()) // Unchanged
}
Custom Type Validation
type Order struct {
ID string `json:"id"`
Amount float64 `json:"amount"`
}
func (o Order) Validate() error {
if o.ID == "" {
return fmt.Errorf("ID required")
}
return nil
}
func TestValidationError(t *testing.T) {
// Response missing required ID
provider := zyn.NewMockProviderWithResponse(`{
"id": "",
"amount": 100,
"confidence": 0.9,
"reasoning": ["extracted"]
}`)
extractor, _ := zyn.Extract[Order]("extract order", provider)
session := zyn.NewSession()
_, err := extractor.Fire(ctx, session, "Order for $100")
assert.Error(t, err)
assert.Contains(t, err.Error(), "ID required")
}
Integration Tests
For tests against real providers:
func TestRealProvider(t *testing.T) {
if os.Getenv("OPENAI_API_KEY") == "" {
t.Skip("OPENAI_API_KEY not set")
}
provider := openai.New(openai.Config{
APIKey: os.Getenv("OPENAI_API_KEY"),
Model: "gpt-4o-mini",
})
synapse, _ := zyn.Binary("Is this a valid email?", provider,
zyn.WithTimeout(30*time.Second),
)
session := zyn.NewSession()
result, err := synapse.Fire(context.Background(), session, "test@example.com")
assert.NoError(t, err)
assert.True(t, result)
}
Benchmarks
func BenchmarkSynapseFire(b *testing.B) {
provider := zynt.NewSequencedProvider(
zynt.NewResponseBuilder().
WithDecision(true).
WithConfidence(0.9).
WithReasoning("ok").
Build(),
)
synapse, _ := zyn.Binary("question", provider)
b.ResetTimer()
for i := 0; i < b.N; i++ {
session := zyn.NewSession()
synapse.Fire(context.Background(), session, "input")
}
}
Next Steps
- Best Practices - Production guidelines
- Testing Package Reference - Full API