Google ADK TypeScript Ajanlarını Cümleleri Kovalamadan Test Etme Rehberi
Google ADK (Agent Development Kit) ile geliştirilen TypeScript tabanlı ajanlar, modern uygulamaların ve etkileşimli sistemlerin kalbinde yer alıyor. Ancak bu akıllı ajanları test etmek, özellikle de karmaşık diyalog akışları ve dış servis entegrasyonları söz konusu olduğunda, çoğu zaman “cümleleri kovalamak” gibi zorlayıcı bir sürece dönüşebilir. Bu makale, TypeScript ajanlarınızı daha verimli, güvenilir ve modüler bir şekilde test etmeniz için kapsamlı bir rehber sunarak, geliştirme sürecinizi hızlandırmayı ve kod kalitenizi artırmayı hedefliyor.
Google ADK TypeScript ajanları, kullanıcılarla etkileşim kuran, belirli görevleri otomatikleştiren veya karmaşık iş mantığını yürüten yazılım bileşenleridir. Bu ajanlar genellikle diyalog sistemleri (örneğin, chatbotlar, sesli asistanlar), otomasyon betikleri veya arka plan servisleri olarak işlev görürler. TypeScript’in sağladığı tip güvenliği ve modern dil özellikleri, bu tür ajanların geliştirilmesini kolaylaştırsa da, test süreçleri çoğu zaman göz ardı edilir veya yetersiz kalır. Geleneksel test yaklaşımları, ajanların karmaşık etkileşimlerini ve dış bağımlılıklarını simüle etmede yetersiz kalabilir, bu da hataların üretim ortamına sızmasına yol açabilir. Bu durum, geliştiricilerin saatlerce süren manuel testler yapmasına veya hataları ayıklamak için uzun diyalog akışlarını tekrar tekrar takip etmesine neden olur; bu da makalenin başlığındaki “cümleleri kovalamak” metaforunu ortaya çıkarır. Amacımız, bu zorlukları aşmak ve geliştiricilere, ajanlarının temel iş mantığını, dış entegrasyonlarını ve uçtan uca davranışlarını sistematik bir şekilde test etmeleri için pratik stratejiler ve araçlar sunmaktır. Bu sayede, ajanlarınızın beklenen şekilde çalıştığından emin olabilir, geliştirme döngüsünü kısaltabilir ve daha güvenilir uygulamalar sunabilirsiniz.
Google ADK ve TypeScript Ajanları Neden Önemli? Temel Kavramlara Derin Bir Bakış
Google ADK (Agent Development Kit) kavramı, genellikle Google Cloud ekosisteminde, özellikle yapay zeka ve makine öğrenimi servisleriyle etkileşim kuran akıllı ajanların geliştirilmesi için kullanılan bir dizi araç, kütüphane ve çerçeveyi (framework) ifade eder. Bu ajanlar, Dialogflow gibi diyalogsal yapay zeka platformlarından, Google Cloud Functions veya Cloud Run üzerinde çalışan sunucusuz (serverless) arka uçlara kadar geniş bir yelpazede konumlanabilir. Temel amaç, kullanıcı girişlerini işlemek, belirli kararlar almak ve ilgili yanıtları veya eylemleri tetiklemektir. Örneğin, bir müşteri hizmetleri chatbotu, bir sipariş takip sistemi veya bir otomasyon aracı bir Google ADK TypeScript ajanı olarak düşünülebilir.
Peki, bu ajanların geliştirilmesinde neden özellikle TypeScript tercih ediliyor? JavaScript’in tip güvenliği olmayan doğası, özellikle büyük ve karmaşık projelerde, hatalara ve bakım zorluklarına yol açabilir. TypeScript, JavaScript’in üzerine inşa edilmiş, isteğe bağlı statik tip denetimi sunan bir üst kümedir. Bu, geliştiricilerin kodlarına tip bilgisi eklemesine olanak tanır, bu da derleme zamanında potansiyel hataların yakalanmasını sağlar. Bu özellik, özellikle şu açılardan kritik öneme sahiptir:
- Kod Kalitesi ve Bakım Kolaylığı: Tip tanımları sayesinde, kod daha okunabilir ve anlaşılır hale gelir. Bir fonksiyonun hangi türde argümanlar beklediği ve hangi türde değer döndürdüğü açıkça belirtildiği için, farklı geliştiricilerin aynı kod tabanında çalışması kolaylaşır. Bu, uzun vadede kodun sürdürülebilirliğini ve bakımını önemli ölçüde iyileştirir.
- Hata Yakalama: Derleme zamanında tip hatalarını yakalamak, çalışma zamanı (runtime) hatalarının önüne geçer. Bu, özellikle dış servislerle entegrasyonlarda veya karmaşık veri yapıları üzerinde çalışırken kritik bir avantajdır. Örneğin, bir API’den beklenen veri yapısı değiştiğinde, TypeScript bu değişikliği derleme aşamasında fark etmenizi sağlar.
- Geliştirici Deneyimi: Modern IDE’ler (Entegre Geliştirme Ortamı), TypeScript’in tip bilgilerini kullanarak akıllı otomatik tamamlama (autocompletion), yeniden düzenleme (refactoring) ve hata kontrolü gibi özellikler sunar. Bu, geliştirme sürecini hızlandırır ve geliştiricilerin daha verimli çalışmasını sağlar.
- Ölçeklenebilirlik: Büyük ve büyüyen ajan projelerinde, tip güvenliği kodun tutarlılığını korumaya yardımcı olur. Yeni özellikler eklendikçe veya mevcut kod refaktör edildiğinde, tip sistemi olası yan etkileri veya uyumsuzlukları tespit etmeye yardımcı olur, bu da projenin daha ölçeklenebilir olmasını sağlar.
Ancak, TypeScript’in sağladığı avantajlara rağmen, Google ADK TypeScript ajanlarını test etmek kendi içinde zorluklar barındırır. Bu zorluklar genellikle ajanın doğasından kaynaklanır:
- Dış Bağımlılıklar: Ajanlar genellikle Google Cloud API’leri (örneğin, Firestore, Cloud Storage), üçüncü taraf API’ler veya veritabanları gibi dış servislerle etkileşim kurar. Bu bağımlılıklar, test ortamında gerçek servisleri kullanmadan izole testler yazmayı zorlaştırır.
- Asenkron İşlemler: Çoğu ajanın iş mantığı asenkron (asynchronous) işlemler içerir (API çağrıları, veritabanı sorguları). Bu asenkron yapıyı doğru bir şekilde test etmek, test çerçevelerinin (testing frameworks) ve yaklaşımlarının dikkatli kullanımını gerektirir.
- Diyaloga Dayalı Mantık: Özellikle chatbot veya sesli asistan ajanları için, iş mantığı genellikle bir diyalog akışına gömülüdür. Kullanıcının farklı girdilerine veya bağlamına göre ajanın nasıl tepki verdiğini test etmek, “cümleleri kovalamak” anlamına gelen karmaşık senaryoların simülasyonunu gerektirebilir.
- Durum Yönetimi (State Management): Ajanlar, kullanıcı oturumları veya belirli bir görevdeki ilerleme gibi durumları yönetebilir. Bu durumun farklı aşamalarında ajanın davranışını test etmek, test senaryolarını daha karmaşık hale getirir.
- Performans ve Güvenilirlik: Üretim ortamında ajanın hızlı ve hatasız çalışması beklenir. Bu nedenle, sadece fonksiyonel doğruluğu değil, aynı zamanda performans ve hata toleransını da test etmek önemlidir.
Bu zorlukların üstesinden gelmek, doğru test stratejileri ve araçlarını benimsemeyi gerektirir. Bir sonraki bölümde, ajanlarınızı daha modüler ve test edilebilir hale getirecek stratejilere odaklanacağız.
Test Stratejileri: Bir Ajanı Modüler Şekilde Nasıl Test Edebiliriz?
Google ADK TypeScript ajanlarını test etmek, geleneksel yazılım test prensiplerini ajanın özel doğasına uyarlamayı gerektirir. “Cümleleri kovalamadan” etkin bir test süreci oluşturmanın anahtarı, ajanın iş mantığını, dış bağımlılıklarını ve diyalog akışlarını mümkün olduğunca birbirinden ayırarak modüler bir yapı kurmaktır. Bu, testlerin daha hızlı, daha güvenilir ve daha kolay yazılabilir olmasını sağlar. Temel olarak üç ana test seviyesi üzerinde durulur: birim testleri (unit tests), entegrasyon testleri (integration tests) ve uçtan uca testler (end-to-end tests).
Birim Testleri (Unit Tests): Saf Fonksiyonları ve İş Mantığını İzole Etme
Birim testleri, uygulamanızın en küçük, bağımsız ve test edilebilir birimlerini (genellikle fonksiyonlar veya metotlar) test etmeye odaklanır. Bir ADK TypeScript ajanı bağlamında, bu, diyalog akışından veya dış bağımlılıklardan tamamen izole edilmiş saf iş mantığı fonksiyonlarını test etmek anlamına gelir. Örneğin, bir sipariş takip ajanının sipariş numarasını doğrulayan bir fonksiyonu veya bir ürün fiyatını hesaplayan bir fonksiyonu birim testi ile test edilebilir.
Birim testlerinin temel avantajları şunlardır:
- Hız: Dış bağımlılıkları olmadığı için çok hızlı çalışırlar.
- İzolasyon: Her birim bağımsız olarak test edildiği için, bir testin başarısız olması doğrudan o birimdeki bir hatayı işaret eder.
- Hata Yeri Tespiti: Hatanın nerede olduğunu bulmak çok daha kolaydır.
- Refaktör Güvenliği: Kodu refaktör ederken, mevcut birim testleri yeni değişikliklerin mevcut işlevselliği bozup bozmadığını kontrol etmeye yardımcı olur.
Bir ajan geliştirirken, iş mantığını mümkün olduğunca diyalog akışından ve platforma özgü koddan ayırmaya çalışmalısınız. Örneğin, bir kullanıcının isteğini işleyecek bir fonksiyon, bu isteği Dialogflow’dan mı yoksa başka bir kaynaktan mı geldiğinden bağımsız olarak çalışmalıdır. Bu “saf fonksiyonlar” yaklaşımı, test edilebilirliği artırır.
Entegrasyon Testleri (Integration Tests): Bağımlılıklarla Etkileşimi Doğrulama
Entegrasyon testleri, farklı birimlerin veya modüllerin birbiriyle ve dış sistemlerle (veritabanları, API’ler, diğer servisler) nasıl etkileşim kurduğunu test eder. Bir ADK TypeScript ajanı için bu, ajanın Google Cloud API’leri (Firestore, Cloud Storage gibi), üçüncü taraf API’ler veya özel arka uç servisleriyle doğru şekilde iletişim kurup kurmadığını kontrol etmek anlamına gelir. Entegrasyon testlerinde, dış bağımlılıkların gerçek versiyonlarını kullanmak yerine, genellikle “mock” (sahte) veya “stub” (taklit) nesneler kullanılır. Bu sayede testler daha hızlı ve izole bir şekilde çalışır.
Entegrasyon testlerinin avantajları:
- Modül Etkileşimlerini Doğrulama: Farklı bileşenlerin bir araya geldiğinde doğru çalıştığından emin olur.
- Dış Servis Etkileşimlerini Simüle Etme: Gerçek servisleri kullanmadan dış bağımlılıklarla olan iletişimi test etme olanağı sunar.
- Daha Kapsamlı Hata Yakalama: Birim testlerinin yakalayamadığı, farklı modüller arasındaki etkileşimden kaynaklanan hataları tespit eder.
Örneğin, bir ajanın bir kullanıcının sipariş bilgilerini Firestore’dan çekmesi gerekiyorsa, entegrasyon testi, Firestore istemcisinin doğru parametrelerle çağrıldığını ve beklenen veriyi döndürdüğünü (mocklanmış bir cevapla) doğrular. Bu, Firestore’un kendisinin doğru çalıştığını test etmek yerine, ajanın Firestore ile olan arayüzünün doğru çalıştığını test eder.
Uçtan Uca Testler (End-to-End Tests – E2E): Gerçek Kullanıcı Deneyimini Simüle Etme
Uçtan uca testler, bir uygulamanın tüm akışını, kullanıcının bakış açısından test eder. Bir ADK TypeScript ajanı için bu, bir kullanıcının ajana bir istek göndermesinden (örneğin, bir mesaj veya sesli komut) ajanın nihai yanıtı veya eylemi gerçekleştirmesine kadar olan tüm süreci simüle etmek anlamına gelir. Bu testler, genellikle ajanın dağıtıldığı platformda (Dialogflow konsolu, özel bir web arayüzü vb.) gerçek veya sanal bir kullanıcı etkileşimini taklit eder.
E2E testlerinin faydaları:
- Gerçek Dünya Senaryoları: Uygulamanın üretim ortamında nasıl davranacağını en doğru şekilde yansıtır.
- Sistem Entegrasyonunu Doğrulama: Tüm sistemin (ön uç, arka uç, veritabanları, dış servisler) bir bütün olarak çalıştığından emin olur.
- Kullanıcı Deneyimini Onaylama: Kullanıcının beklediği akışın ve yanıtların doğru olduğunu doğrular.
Ancak E2E testleri genellikle yavaştır, bakımı zordur ve bir hata durumunda hatanın tam yerini tespit etmek daha karmaşıktır. Bu nedenle, genellikle birim ve entegrasyon testleriyle birlikte, kritik kullanıcı akışlarını kapsayacak şekilde sınırlı sayıda kullanılırlar. Örneğin, bir kullanıcının “Siparişimi takip et” demesiyle başlayan, sipariş numarasını girmesiyle devam eden ve ajanın sipariş durumunu bildirmesiyle biten bir senaryo E2E testi ile kapsanabilir.
Test Piramidi Yaklaşımı
Etkin bir test stratejisi için genellikle “Test Piramidi” yaklaşımı önerilir:
- Taban (Geniş): Birim Testleri: En çok sayıda ve en hızlı testlerdir. İş mantığının çoğunu kapsar.
- Orta Kısım: Entegrasyon Testleri: Orta sayıda ve orta hızda testlerdir. Modül etkileşimlerini ve dış bağımlılıkları mocklayarak test eder.
- Tepe (Dar): Uçtan Uca Testler: En az sayıda ve en yavaş testlerdir. Kritik kullanıcı akışlarını kapsar.
Bu piramit, testlerin maliyetini ve hızını dengeleyerek en iyi kapsayıcılığı sağlar. Bir sonraki bölümde, bu stratejileri uygulamak için TypeScript ile bir test ortamının nasıl kurulacağını adım adım inceleyeceğiz.
TypeScript ile Test Ortamı Kurulumu: Adım Adım Rehber
Google ADK TypeScript ajanlarınızı verimli bir şekilde test etmek için sağlam bir test ortamı kurmak kritik öneme sahiptir. Bu bölüm, popüler bir test çerçevesi olan Jest’i kullanarak TypeScript projeleriniz için adım adım bir test ortamı kurulumunu ve temel birim testi örneğini içerecektir. Jest, sıfır yapılandırma (zero-config) felsefesi, hızlı çalışma süresi ve güçlü mocking yetenekleri sayesinde TypeScript projeleri için ideal bir seçimdir.
1. Proje Yapısı ve Bağımlılıkların Kurulumu
Öncelikle, yeni bir TypeScript projesi başlatalım veya mevcut projemizin kök dizininde olduğumuzdan emin olalım. Gerekli bağımlılıkları yüklemek için terminali kullanın:
npm init -y
npm install typescript ts-node @types/node --save-dev
npm install jest @types/jest ts-jest --save-dev
typescript: TypeScript derleyicisi.ts-node: TypeScript dosyalarını doğrudan çalıştırmak için (testler için genellikle dolaylı kullanılır).@types/node: Node.js için tip tanımları.jest: Test çerçevemiz.@types/jest: Jest için tip tanımları.ts-jest: Jest’in TypeScript dosyalarını anlamasını sağlayan bir önişlemci (preprocessor).
2. TypeScript Yapılandırması (tsconfig.json)
TypeScript derleyicisinin nasıl davranacağını belirtmek için bir tsconfig.json dosyası oluşturun:
// tsconfig.json
{
"compilerOptions": {
"target": "es2020",
"module": "commonjs",
"rootDir": "./src",
"outDir": "./dist",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}
Bu yapılandırma, modern JavaScript özelliklerini kullanmamızı ve dosyalarımızın src dizininde olduğunu varsayar. Test dosyaları için ayrı bir tsconfig.json oluşturmak da mümkündür, ancak çoğu küçük ve orta ölçekli proje için bu yeterlidir.
3. Jest Yapılandırması (jest.config.js)
Jest’in TypeScript ile nasıl çalışacağını belirtmek için proje kök dizinine jest.config.js adında bir dosya ekleyin:
// jest.config.js
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ["**/?(*.)+(spec|test).[jt]s?(x)"],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
collectCoverage: true, // Kod kapsayıcılığı raporlamasını etkinleştirir
coverageDirectory: "coverage",
coveragePathIgnorePatterns: [
"/node_modules/",
"/dist/"
]
};
preset: 'ts-jest': Jest’e TypeScript dosyalarınıts-jestile işlemesini söyler.testEnvironment: 'node': Testlerin Node.js ortamında çalışacağını belirtir (tarayıcı ortamı yerine).testMatch: Jest’in hangi dosyaları test olarak kabul edeceğini tanımlar (genellikle.test.tsveya.spec.tsuzantılı dosyalar).collectCoverage: true: Testlerin kod kapsamını ölçmesini sağlar.
4. Temel Ajan Fonksiyonu ve Birim Testi Örneği
Şimdi bir örnek ajan fonksiyonu ve buna ait bir birim testi yazalım. Diyelim ki ajanızın bir parçası olarak basit bir selamlaşma mesajı üreten bir fonksiyonunuz var.
src/agentLogic.ts:
// src/agentLogic.ts
export function generateGreeting(name?: string): string {
if (name) {
return Merhaba, ${name}! Size nasıl yardımcı olabilirim?;
}
return "Merhaba! Size nasıl yardımcı olabilirim?";
}
export function calculateOrderTotal(items: { price: number; quantity: number }[]): number {
if (!items || items.length === 0) {
return 0;
}
return items.reduce((total, item) => total + (item.price * item.quantity), 0);
}
src/agentLogic.test.ts:
// src/agentLogic.test.ts
import { generateGreeting, calculateOrderTotal } from './agentLogic';
describe('generateGreeting', () => {
test('isim belirtilmediğinde genel bir selamlaşma dönmeli', () => {
expect(generateGreeting()).toBe('Merhaba! Size nasıl yardımcı olabilirim?');
});
test('isim belirtildiğinde kişiselleştirilmiş bir selamlaşma dönmeli', () => {
expect(generateGreeting('Ali')).toBe('Merhaba, Ali! Size nasıl yardımcı olabilirim?');
});
test('boş string isimde genel selamlaşma dönmeli', () => {
expect(generateGreeting('')).toBe('Merhaba, ! Size nasıl yardımcı olabilirim?'); // Kasıtlı olarak boş string ile test ediyoruz
});
});
describe('calculateOrderTotal', () => {
test('boş ürün listesi için 0 dönmeli', () => {
expect(calculateOrderTotal([])).toBe(0);
});
test('tek ürün için doğru toplamı hesaplamalı', () => {
const items = [{ price: 100, quantity: 1 }];
expect(calculateOrderTotal(items)).toBe(100);
});
test('birden fazla ürün için doğru toplamı hesaplamalı', () => {
const items = [
{ price: 50, quantity: 2 },
{ price: 25, quantity: 3 }
];
expect(calculateOrderTotal(items)).toBe(50 * 2 + 25 * 3); // 100 + 75 = 175
});
test('sıfır fiyatlı ürünleri doğru işlemeli', () => {
const items = [
{ price: 0, quantity: 5 },
{ price: 10, quantity: 1 }
];
expect(calculateOrderTotal(items)).toBe(10);
});
test('negatif fiyatlı ürünler için hata döndürmese de doğru toplamı hesaplamalı (iş mantığına bağlı)', () => {
const items = [
{ price: -10, quantity: 2 }, // Negatif fiyatlar gerçek dünyada olmamalı, ama test ediyoruz
{ price: 20, quantity: 1 }
];
expect(calculateOrderTotal(items)).toBe(-20 + 20); // 0
});
});
5. Testleri Çalıştırma
package.json dosyanıza bir betik (script) ekleyerek testleri kolayca çalıştırabilirsiniz:
// package.json
{
// ... diğer alanlar
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
},
// ... diğer alanlar
}
Şimdi terminalde npm test komutunu çalıştırarak testlerinizi çalıştırabilirsiniz. Çıktı, testlerin başarı durumunu ve kod kapsamı raporunu gösterecektir.
Bu kurulumla, ajanınızın temel iş mantığını izole edilmiş bir şekilde test etmeye başlayabilirsiniz. Bir sonraki adım, dış bağımlılıkları olan daha karmaşık senaryoları nasıl ele alacağımızı öğrenmek olacaktır.
Gerçek Dünya Senaryolarında Ajan Testi: Vaka Analizleri ve Uygulamalar
Bir Google ADK TypeScript ajanının gerçek dünya senaryolarında nasıl test edileceğini anlamak, soyut test kavramlarını somutlaştırmak için önemlidir. Bu bölümde, üç farklı vaka analizi üzerinden ajan test stratejilerini uygulamalı olarak ele alacağız. Her senaryo, farklı türde bağımlılıklar ve iş mantığı içerecek ve bu zorlukların üstesinden gelmek için birim ve entegrasyon testlerinin nasıl kullanılabileceğini gösterecektir.
Vaka Analizi 1: E-ticaret Sipariş Takip Ajanı
Senaryo: Bir e-ticaret platformu için geliştirilen bir chatbot ajanı, kullanıcının sipariş numarasını alarak ilgili siparişin durumunu (örneğin, “Hazırlanıyor”, “Kargoda”, “Teslim Edildi”) harici bir sipariş yönetimi API’sinden sorgular ve kullanıcıya geri bildirimde bulunur.
Test Edilmesi Gerekenler:
- Sipariş numarasının geçerliliği (format kontrolü).
- Harici API’ye yapılan çağrının doğru parametrelerle yapılması.
- API’den gelen yanıtın doğru şekilde işlenmesi ve kullanıcıya uygun mesajın oluşturulması.
- API’nin hata döndürmesi durumunda ajanın nasıl tepki verdiği.
Uygulama ve Test Yaklaşımı:
Bu senaryoda, ajanın iş mantığını (sipariş numarasını doğrulama ve kullanıcıya mesaj oluşturma) harici API çağrısından ayırmalıyız. API çağrısı kısmı entegrasyon testi ile, diğer kısımlar birim testleri ile ele alınabilir.
src/orderService.ts (Harici API ile etkileşimi simüle eden servis):
// src/orderService.ts
export interface OrderDetails {
orderId: string;
status: 'Hazırlanıyor' | 'Kargoda' | 'Teslim Edildi' | 'İptal Edildi';
estimatedDelivery?: string;
}
export async function fetchOrderDetails(orderId: string): Promise {
// Gerçek bir senaryoda burada harici bir API çağrısı olurdu
// Örneğin: const response = await axios.get(https://api.example.com/orders/${orderId});
// Şimdilik mock bir yanıt döndürelim:
if (orderId === '12345') {
return { orderId: '12345', status: 'Kargoda', estimatedDelivery: '2 gün içinde' };
}
if (orderId === '98765') {
return { orderId: '98765', status: 'Teslim Edildi' };
}
return null; // Sipariş bulunamadı
}
src/agentFunctions.ts (Ajanın iş mantığı):
// src/agentFunctions.ts
import { fetchOrderDetails, OrderDetails } from './orderService';
export function isValidOrderId(orderId: string): boolean {
return /^\d{5}$/.test(orderId); // 5 haneli sayı olduğunu varsayalım
}
export async function getOrderStatusMessage(orderId: string): Promise {
if (!isValidOrderId(orderId)) {
return 'Lütfen geçerli bir sipariş numarası girin (örneğin: 12345).';
}
const orderDetails = await fetchOrderDetails(orderId);
if (!orderDetails) {
return "${orderId}" numaralı sipariş bulunamadı. Lütfen tekrar kontrol edin.;
}
let message = Siparişiniz (${orderDetails.orderId}) şu anda "${orderDetails.status}" durumundadır.;
if (orderDetails.estimatedDelivery) {
message += Tahmini teslimat: ${orderDetails.estimatedDelivery}.;
}
return message;
}
src/agentFunctions.test.ts (Testler):
// src/agentFunctions.test.ts
import { isValidOrderId, getOrderStatusMessage } from './agentFunctions';
import * as OrderService from './orderService'; // orderService modülünü mocklamak için
// OrderService modülünü tamamen mockluyoruz
jest.mock('./orderService');
describe('isValidOrderId', () => {
test('geçerli sipariş numarasını doğrulamalı', () => {
expect(isValidOrderId('12345')).toBe(true);
});
test('geçersiz sipariş numarasını reddetmeli (yanlış uzunluk)', () => {
expect(isValidOrderId('123')).toBe(false);
expect(isValidOrderId('123456')).toBe(false);
});
test('geçersiz sipariş numarasını reddetmeli (harf içeriyor)', () => {
expect(isValidOrderId('abcde')).toBe(false);
});
});
describe('getOrderStatusMessage', () => {
// Her testten önce mocklanmış fonksiyonları temizle
beforeEach(() => {
jest.clearAllMocks();
});
test('geçersiz sipariş numarası için uyarı mesajı dönmeli', async () => {
const message = await getOrderStatusMessage('123');
expect(message).toBe('Lütfen geçerli bir sipariş numarası girin (örneğin: 12345).');
// fetchOrderDetails fonksiyonunun çağrılmadığını doğrula
expect(OrderService.fetchOrderDetails).not.toHaveBeenCalled();
});
test('sipariş bulunamadığında uyarı mesajı dönmeli', async () => {
// fetchOrderDetails fonksiyonunu null döndürecek şekilde mockla
(OrderService.fetchOrderDetails as jest.Mock).mockResolvedValue(null);
const message = await getOrderStatusMessage('54321');
expect(message).toBe('"54321" numaralı sipariş bulunamadı. Lütfen tekrar kontrol edin.');
expect(OrderService.fetchOrderDetails).toHaveBeenCalledWith('54321');
});
test('sipariş durumu ve tahmini teslimat ile mesaj dönmeli', async () => {
const mockOrder: OrderService.OrderDetails = {
orderId: '12345',
status: 'Kargoda',
estimatedDelivery: '2 gün içinde'
};
(OrderService.fetchOrderDetails as jest.Mock).mockResolvedValue(mockOrder);
const message = await getOrderStatusMessage('12345');
expect(message).toBe('Siparişiniz (12345) şu anda "Kargoda" durumundadır. Tahmini teslimat: 2 gün içinde.');
expect(OrderService.fetchOrderDetails).toHaveBeenCalledWith('12345');
});
test('sadece sipariş durumu ile mesaj dönmeli (tahmini teslimat yoksa)', async () => {
const mockOrder: OrderService.OrderDetails = {
orderId: '98765',
status: 'Teslim Edildi'
};
(OrderService.fetchOrderDetails as jest.Mock).mockResolvedValue(mockOrder);
const message = await getOrderStatusMessage('98765');
expect(message).toBe('Siparişiniz (98765) şu anda "Teslim Edildi" durumundadır.');
expect(OrderService.fetchOrderDetails).toHaveBeenCalledWith('98765');
});
});
Bu örnekte, isValidOrderId fonksiyonu tamamen bağımsız olduğu için birim testiyle kolayca test edilebilir. getOrderStatusMessage fonksiyonu ise fetchOrderDetails‘e bağımlıdır. Bu bağımlılığı, Jest’in jest.mock özelliği ile sahte (mock) bir versiyonla değiştirerek, gerçek API çağrısı yapmadan ajanın mantığını test edebiliyoruz. Bu, entegrasyon testi seviyesinde bir yaklaşımdır, ancak dış bağımlılığı izole ettiğimiz için hızlı ve kontrol edilebilir kalır.
Vaka Analizi 2: Müşteri Hizmetleri Chatbotu – Sıkça Sorulan Sorular (SSS) Ajanı
Senaryo: Bir müşteri hizmetleri chatbotu, kullanıcının sorduğu soruları belirli anahtar kelimelerle eşleştirerek önceden tanımlanmış SSS yanıtlarını döndürür. Eğer uygun bir SSS bulunamazsa, kullanıcıyı bir canlı temsilciye yönlendirir.
Test Edilmesi Gerekenler:
- Farklı kullanıcı soruları için doğru SSS yanıtının eşleştirilmesi.
- Anahtar kelime eşleşmesi olmadığında canlı temsilciye yönlendirme mekanizmasının tetiklenmesi.
- Büyük/küçük harf duyarlılığı veya benzeri kenar durumlar.
Uygulama ve Test Yaklaşımı:
Bu ajan, özellikle metin işleme ve eşleştirme mantığına odaklanır. İş mantığını izole edip, sadece bu mantığı test edebiliriz.
src/faqService.ts:
// src/faqService.ts
export interface FAQItem {
keywords: string[];
answer: string;
}
const faqs: FAQItem[] = [
{ keywords: ['sipariş', 'takip'], answer: 'Siparişinizi takip etmek için sipariş numaranızı girmeniz yeterlidir.' },
{ keywords: ['iade', 'ürün'], answer: 'İade işlemleri için lütfen iade politikamızı inceleyin veya müşteri hizmetleri ile iletişime geçin.' },
{ keywords: ['kargo', 'teslimat'], answer: 'Kargo ve teslimat süreleri ürününüze ve adresinize göre değişiklik gösterebilir.' },
];
export function findFAQAnswer(question: string): string | null {
const lowerCaseQuestion = question.toLowerCase();
for (const faq of faqs) {
if (faq.keywords.some(keyword => lowerCaseQuestion.includes(keyword))) {
return faq.answer;
}
}
return null;
}
src/faqService.test.ts:
// src/faqService.test.ts
import { findFAQAnswer } from './faqService';
describe('findFAQAnswer', () => {
test('sipariş ile ilgili soruya doğru yanıt dönmeli', () => {
expect(findFAQAnswer('siparişimi takip edebilir miyim?')).toBe('Siparişinizi takip etmek için sipariş numaranızı girmeniz yeterlidir.');
});
test('iade ile ilgili soruya doğru yanıt dönmeli', () => {
expect(findFAQAnswer('ürün iadesi nasıl yapabilirim?')).toBe('İade işlemleri için lütfen iade politikamızı inceleyin veya müşteri hizmetleri ile iletişime geçin.');
});
test('büyük/küçük harf duyarlılığını doğru işlemeli', () => {
expect(findFAQAnswer('SİPARİŞİM nerede?')).toBe('Siparişinizi takip etmek için sipariş numaranızı girmeniz yeterlidir.');
});
test('eşleşme bulunamadığında null dönmeli', () => {
expect(findFAQAnswer('ürünleriniz kaliteli mi?')).toBeNull();
});
test('birden fazla anahtar kelime içeren soruya ilk eşleşen yanıtı dönmeli', () => {
// 'sipariş' ve 'iade' içeren bir soru, 'sipariş' önce eşleştiği için onun yanıtı gelmeli
expect(findFAQAnswer('siparişimi iade etmek istiyorum')).toBe('Siparişinizi takip etmek için sipariş numaranızı girmeniz yeterlidir.');
});
});
Bu senaryoda, findFAQAnswer fonksiyonu saf bir fonksiyon olduğu için dış bağımlılığı yoktur ve doğrudan birim testleriyle test edilebilir. Bu, ajanın çekirdek mantığının hızlı ve güvenilir bir şekilde doğrulanmasını sağlar.
Vaka Analizi 3: Veri Toplama Ajanı – Form Doldurma
Senaryo: Bir ajan, kullanıcılardan ad, soyad, e-posta gibi bilgileri adım adım toplayarak bir kayıt formu doldurur. Her adımda kullanıcının girdiği verinin geçerliliğini kontrol eder.
Test Edilmesi Gerekenler:
- Her bir veri alanının (ad, e-posta vb.) doğrulama kuralları.
- Kullanıcıya yanlış girişlerde uygun hata mesajlarının verilmesi.
- Tüm veriler toplandığında kayıt işleminin tetiklenmesi (mock bir servise).
Uygulama ve Test Yaklaşımı:
Bu senaryoda, her bir veri alanının doğrulama mantığı birim testleriyle, kayıt işlemini tetikleyen kısım ise entegrasyon testiyle ele alınabilir.
src/validationService.ts:
// src/validationService.ts
export function isValidEmail(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
export function isValidName(name: string): boolean {
return name.trim().length > 2;
}
src/registrationAgent.ts (Ajanın kayıt mantığı):
// src/registrationAgent.ts
import { isValidEmail, isValidName } from './validationService';
export interface UserRegistrationData {
name: string;
email: string;
}
// Mock bir kayıt servisi
async function registerUser(data: UserRegistrationData): Promise<{ success: boolean; message: string }> {
return new Promise((resolve) => {
setTimeout(() => {
if (data.email.includes('error')) {
resolve({ success: false, message: 'Kayıt başarısız oldu: E-posta zaten kullanımda.' });
} else {
resolve({ success: true, message: Kullanıcı ${data.name} başarıyla kaydedildi. });
}
}, 100); // Asenkron bir işlem simülasyonu
});
}
export async function processRegistration(name: string, email: string): Promise {
if (!isValidName(name)) {
return 'Lütfen geçerli bir isim girin.';
}
if (!isValidEmail(email)) {
return 'Lütfen geçerli bir e-posta adresi girin.';
}
const result = await registerUser({ name, email });
if (result.success) {
return result.message;
} else {
return Kayıt işlemi sırasında bir hata oluştu: ${result.message};
}
}
src/registrationAgent.test.ts:
// src/registrationAgent.test.ts
import { processRegistration } from './registrationAgent';
import * as ValidationService from './validationService';
// ValidationService'i mocklamıyoruz çünkü saf fonksiyonlar ve doğrudan test edilebilirler.
// Ancak registerUser fonksiyonunu içeren registrationAgent modülündeki registerUser'ı mocklayabiliriz.
// processRegistration içinde çağrılan registerUser fonksiyonunu mocklamak için
// doğrudan processRegistration'ı içeren dosyayı import edip içindeki fonksiyonu spyOn ile mocklayabiliriz.
// Ancak daha iyi bir yaklaşım, registerUser'ı ayrı bir modül olarak dışa aktarmak ve onu mocklamaktır.
// Bu örnekte, processRegistration içinde yerleşik bir fonksiyon gibi davranan registerUser'ı
// ayrı bir mock edilebilir servis olarak düşünelim veya doğrudan processRegistration'ı test edelim.
describe('processRegistration', () => {
// isValidName ve isValidEmail fonksiyonlarını mocklayabiliriz, ancak bu örnekte
// doğrudan çağrılıp sonuçlarına göre test edeceğiz.
// Gerçek bir senaryoda, registerUser'ı mocklamak daha yaygın olacaktır.
test('geçersiz isim için hata mesajı dönmeli', async () => {
const result = await processRegistration('a', 'test@example.com');
expect(result).toBe('Lütfen geçerli bir isim girin.');
});
test('geçersiz e-posta için hata mesajı dönmeli', async () => {
const result = await processRegistration('Ali Can', 'invalid-email');
expect(result).toBe('Lütfen geçerli bir e-posta adresi girin.');
});
test('başarılı kayıt için onay mesajı dönmeli', async () => {
// Burada registerUser'ı mocklamış gibi davranıyoruz.
// Gerçek bir senaryoda, registerUser'ı dışa aktarıp jest.mock ile mocklardık.
const result = await processRegistration('Ayşe Yılmaz', 'ayse@example.com');
expect(result).toBe('Kullanıcı Ayşe Yılmaz başarıyla kaydedildi.');
});
test('kayıt servisi hata döndürdüğünde hata mesajı dönmeli', async () => {
// Hata durumunu simüle etmek için özel bir e-posta kullanıyoruz
const result = await processRegistration('Hata Test', 'error@example.com');
expect(result).toBe('Kayıt işlemi sırasında bir hata oluştu: Kayıt başarısız oldu: E-posta zaten kullanımda.');
});
});
Bu vaka analizleri, ajanlarınızın farklı bileşenlerini nasıl izole edeceğinizi ve bunları birim veya entegrasyon testleriyle nasıl kapsayacağınızı göstermektedir. Bağımlılıkları mocklamak, testlerin hızlı ve güvenilir kalmasını sağlarken, ajanın temel işlevselliğini ve dış servislerle etkileşimini doğrular. Bir sonraki bölümde, bu test tekniklerini daha da ileriye taşıyacak gelişmiş yöntemlere odaklanacağız.
İleri Düzey Test Teknikleri: Mocking, Stubbing ve Spy Kullanımı
Google ADK TypeScript ajanları genellikle karmaşık dış bağımlılıklarla (API’ler, veritabanları, diğer Google Cloud servisleri) etkileşim kurar ve asenkron işlemler gerçekleştirir. Bu tür senaryolarda, sadece birim testleri yeterli olmayabilir. İleri düzey test teknikleri olan mocking, stubbing ve spy kullanımı, bu bağımlılıkları kontrol altına almanıza ve asenkron kodunuzu güvenilir bir şekilde test etmenize olanak tanır. Jest test çerçevesi, bu teknikleri uygulamak için güçlü araçlar sunar.
Mocking (Sahte Nesne Oluşturma)
Mocking, bir bağımlılığın (örneğin, bir servis veya modül) gerçek uygulamasını, test sırasında kontrol edebileceğiniz sahte bir versiyonla değiştirmek anlamına gelir. Mock’lar sadece belirli bir değeri döndürmekle kalmaz, aynı zamanda kendilerine yapılan çağrıları da kaydedebilirler (kaç kez çağrıldı, hangi argümanlarla çağrıldı vb.). Bu, bir fonksiyonun dış bir servisle doğru şekilde etkileşim kurup kurmadığını doğrulamak için çok kullanışlıdır.
Jest ile Modül Mocklama:
Diyelim ki bir ajanın Google Cloud Firestore ile etkileşime giren bir dataService.ts modülü var. Bu modülü test ederken gerçek Firestore’a bağlanmak istemeyiz.
src/dataService.ts:
// src/dataService.ts
// Gerçek bir senaryoda burada Firebase Admin SDK veya Google Cloud Firestore SDK kullanılacaktı.
// import { Firestore } from '@google-cloud/firestore';
// const db = new Firestore();
export async function getUserData(userId: string): Promise<{ name: string; email: string } | null> {
// Gerçek Firestore sorgusu burada olurdu
// const docRef = db.collection('users').doc(userId);
// const doc = await docRef.get();
// return doc.exists ? doc.data() as { name: string; email: string } : null;
return new Promise(resolve => setTimeout(() => {
if (userId === 'user123') {
resolve({ name: 'Ahmet Yılmaz', email: 'ahmet@example.com' });
} else {
resolve(null);
}
}, 50));
}
export async function updateUserData(userId: string, data: { name?: string; email?: string }): Promise {
// Gerçek Firestore güncelleme işlemi burada olurdu
// await db.collection('users').doc(userId).update(data);
return new Promise(resolve => setTimeout(resolve, 50));
}
src/agentHandler.ts (Ajanın bu servisi kullandığı yer):
// src/agentHandler.ts
import { getUserData, updateUserData } from './dataService';
export async function handleUserProfileRequest(userId: string): Promise {
const userData = await getUserData(userId);
if (!userData) {
return 'Kullanıcı profili bulunamadı.';
}
return Kullanıcı Adı: ${userData.name}, E-posta: ${userData.email};
}
export async function updateUserName(userId: string, newName: string): Promise {
await updateUserData(userId, { name: newName });
return Kullanıcı ${userId} için isim "${newName}" olarak güncellendi.;
}
src/agentHandler.test.ts (Test dosyası):
// src/agentHandler.test.ts
import { handleUserProfileRequest, updateUserName } from './agentHandler';
import * as DataService from './dataService'; // Mocklamak istediğimiz modül
// dataService modülünü mockluyoruz
jest.mock('./dataService');
describe('handleUserProfileRequest', () => {
beforeEach(() => {
jest.clearAllMocks(); // Her testten önce mock çağrılarını temizle
});
test('kullanıcı verisi bulunduğunda doğru mesajı dönmeli', async () => {
// getUserData fonksiyonunun mocklanmış davranışını tanımla
(DataService.getUserData as jest.Mock).mockResolvedValue({ name: 'Deniz', email: 'deniz@example.com' });
const message = await handleUserProfileRequest('user456');
expect(message).toBe('Kullanıcı Adı: Deniz, E-posta: deniz@example.com');
expect(DataService.getUserData).toHaveBeenCalledWith('user456'); // Doğru argümanla çağrıldığını kontrol et
expect(DataService.getUserData).toHaveBeenCalledTimes(1); // Bir kez çağrıldığını kontrol et
});
test('kullanıcı verisi bulunamadığında uygun mesajı dönmeli', async () => {
(DataService.getUserData as jest.Mock).mockResolvedValue(null); // Null dönecek şekilde mockla
const message = await handleUserProfileRequest('nonexistentUser');
expect(message).toBe('Kullanıcı profili bulunamadı.');
expect(DataService.getUserData).toHaveBeenCalledWith('nonexistentUser');
});
});
describe('updateUserName', () => {
beforeEach(() => {
jest.clearAllMocks();
});
test('kullanıcı adı başarıyla güncellendiğinde onay mesajı dönmeli', async () => {
// updateUserData fonksiyonunun mocklanmış davranışını tanımla
(DataService.updateUserData as jest.Mock).mockResolvedValue(undefined); // void bir fonksiyon mock'u
const message = await updateUserName('user789', 'Yeni İsim');
expect(message).toBe('Kullanıcı user789 için isim "Yeni İsim" olarak güncellendi.');
expect(DataService.updateUserData).toHaveBeenCalledWith('user789', { name: 'Yeni İsim' });
expect(DataService.updateUserData).toHaveBeenCalledTimes(1);
});
});
Stubbing (Taklit Nesne Oluşturma)
Stubbing, bir fonksiyonun veya metodun belirli bir girdi için sabit bir çıktı döndürmesini sağlamaktır. Mocking’e benzerdir ancak stub’lar genellikle çağrı geçmişini kaydetmez veya diğer davranışları kontrol etmez. Sadece belirli bir sonuç döndürmekle ilgilenirler. Jest’te jest.fn().mockReturnValue() veya .mockResolvedValue() gibi yöntemlerle stub’lar oluşturulabilir.
Yukarıdaki örnekte (DataService.getUserData as jest.Mock).mockResolvedValue(...) aslında bir stubbing örneğidir; belirli bir senaryo için belirli bir değer döndürmesini sağlıyoruz.
Spies (Casus Fonksiyonlar)
Spy’lar, mevcut bir fonksiyonu değiştirmeden ona yapılan çağrıları izlemenize olanak tanır. Bir fonksiyonun çağrılıp çağrılmadığını, kaç kez çağrıldığını ve hangi argümanlarla çağrıldığını kontrol etmek istediğinizde kullanışlıdır. Gerçek fonksiyonun çalışmasına izin verirken, aynı zamanda onun üzerindeki etkileşimi de gözlemlersiniz.
Jest ile Spy Kullanımı:
Diyelim ki bir ajanın bir hata durumunda bir loglama fonksiyonunu çağırdığından emin olmak istiyoruz, ancak gerçek loglama işleminin test sırasında gerçekleşmesini istemiyoruz.
src/logger.ts:
// src/logger.ts
export function logError(message: string, context?: any) {
console.error(ERROR: ${message}, context);
// Gerçek bir uygulamada burada bir loglama servisine (Cloud Logging vb.) gönderim olurdu.
}
src/errorAgent.ts (Hata durumunda loglama yapan ajan):
// src/errorAgent.ts
import { logError } from './logger';
export function processCriticalOperation(data: any): string {
if (!data || !data.isValid) {
logError('Kritik işlem için geçersiz veri alındı.', { data });
return 'İşlem başarısız oldu: Geçersiz veri.';
}
// Başarılı işlem mantığı
return 'İşlem başarıyla tamamlandı.';
}
src/errorAgent.test.ts:
// src/errorAgent.test.ts
import { processCriticalOperation } from './errorAgent';
import * as Logger from './logger'; // Spy'layacağımız modül
describe('processCriticalOperation', () => {
let logErrorSpy: jest.SpyInstance;
beforeEach(() => {
// Logger.logError fonksiyonunu spy'la
logErrorSpy = jest.spyOn(Logger, 'logError');
logErrorSpy.mockImplementation(() => {}); // Gerçek loglama işlemini engelle
});
afterEach(() => {
logErrorSpy.mockRestore(); // Spy'ı orijinal haline geri döndür
});
test('geçersiz veri ile çağrıldığında hata loglamalı ve uygun mesajı dönmeli', () => {
const result = processCriticalOperation({ isValid: false });
expect(result).toBe('İşlem başarısız oldu: Geçersiz veri.');
expect(logErrorSpy).toHaveBeenCalledTimes(1);
expect(logErrorSpy).toHaveBeenCalledWith('Kritik işlem için geçersiz veri alındı.', { data: { isValid: false } });
});
test('geçerli veri ile çağrıldığında hata loglamamalı ve başarılı mesajı dönmeli', () => {
const result = processCriticalOperation({ isValid: true });
expect(result).toBe('İşlem başarıyla tamamlandı.');
expect(logErrorSpy).not.toHaveBeenCalled(); // Hata loglanmamalı
});
});
Bu örnekte, jest.spyOn ile Logger.logError fonksiyonunu izliyoruz. mockImplementation(() => {}) ile gerçek console.error çağrısını engelliyoruz, böylece test çıktımız kirlenmiyor. toHaveBeenCalled gibi matcher’lar ile fonksiyonun doğru şekilde çağrılıp çağrılmadığını kontrol ediyoruz.
Asenkron Kodun Etkin Testi
ADK ajanları sıklıkla asenkron işlemlerle uğraşır (API çağrıları, veritabanı işlemleri). Jest, asenkron testleri kolaylaştırmak için çeşitli yöntemler sunar:
- Promises:
async/awaitveya.then().catch()zincirlerini kullanarak testler yazabilirsiniz. Jest, bir testin Promise döndürdüğünü otomatik olarak algılar ve Promise çözümlenene kadar bekler. .resolves/.rejectsMatcher’ları: Bir Promise’in başarıyla çözümlenmesini veya reddedilmesini doğrudan beklemek için bu matcher’ları kullanabilirsiniz.
// Asenkron fonksiyon örneği
async function fetchData(): Promise {
return new Promise(resolve => setTimeout(() => resolve('Veri başarıyla çekildi.'), 100));
}
// Test
test('asenkron veri çekme başarılı olmalı', async () => {
await expect(fetchData()).resolves.toBe('Veri başarıyla çekildi.');
});
async function fetchFailingData(): Promise {
return new Promise((_, reject) => setTimeout(() => reject(new Error('Veri çekme hatası.')), 100));
}
test('asenkron veri çekme başarısız olmalı', async () => {
await expect(fetchFailingData()).rejects.toThrow('Veri çekme hatası.');
});
Bu ileri düzey teknikler, ajanlarınızın karmaşık bağımlılıklarını ve asenkron doğasını kontrol altına almanıza yardımcı olur, böylece testleriniz daha güvenilir, hızlı ve kapsamlı hale gelir. Bu teknikleri ustaca kullanarak, “cümleleri kovalamak” yerine ajanın çekirdek mantığına odaklanabilirsiniz.
Sonuç ve Sıkça Sorulan Sorular
Google ADK TypeScript ajanlarını test etmek, modern yazılım geliştirme süreçlerinin ayrılmaz bir parçasıdır ve “cümleleri kovalamadan” etkin bir şekilde yapılabilir. Bu makale boyunca, TypeScript’in tip güvenliği ve modüler yapısının ajan geliştirme ve test etme süreçlerine nasıl değer kattığını, birim, entegrasyon ve uçtan uca test stratejilerinin nasıl uygulanacağını ve Jest gibi güçlü bir test çerçevesiyle ileri düzey mocking, stubbing ve spy tekniklerinin nasıl kullanılacağını detaylı bir şekilde inceledik. Gerçek dünya senaryoları üzerinden sunduğumuz vaka analizleri, soyut kavramları somutlaştırmanıza ve kendi projelerinizde benzer yaklaşımları uygulamanıza yardımcı olmayı hedeflemiştir.
Unutmayın ki sağlam bir test süreci, sadece hataları erken aşamada yakalamakla kalmaz, aynı zamanda kod kalitesini artırır, geliştirme hızını yükseltir ve gelecekteki değişiklikler için bir güvenlik ağı oluşturur. Ajanlarınızın iş mantığını dış bağımlılıklardan ayırarak, saf fonksiyonlar yazarak ve bu bağımlılıkları test ortamında akıllıca mocklayarak, “cümleleri kovalamak” zorunda kalmadan, ajanlarınızın beklenen şekilde çalıştığından emin olabilirsiniz. Bu yaklaşım, daha güvenilir, bakımı kolay ve ölçeklenebilir Google ADK TypeScript ajanları geliştirmenizin temelini oluşturacaktır. Testlerinizi sürekli olarak güncel tutmak ve CI/CD (Sürekli Entegrasyon/Sürekli Dağıtım) süreçlerinize entegre etmek, bu faydaları maksimize etmenizi sağlayacaktır.
Sıkça Sorulan Sorular
Soru 1: ADK TypeScript ajanları için hangi test çerçevesini önerirsiniz?
Cevap: Google ADK TypeScript ajanları için Jest, sunduğu kapsamlı özellikler, hızlı performans, kolay yapılandırma ve güçlü mocking yetenekleri nedeniyle en çok önerilen test çerçevesidir. TypeScript ile mükemmel uyum sağlar ve asenkron kod testini basitleştirir. Alternatif olarak Mocha veya Vitest gibi çerçeveler de kullanılabilir, ancak Jest genellikle ilk tercih olur.
Soru 2: Entegrasyon testlerinde dış bağımlılıkları nasıl yönetmeliyim?
Cevap: Entegrasyon testlerinde dış bağımlılıkları (veritabanları, harici API’ler, Google Cloud servisleri) yönetmek için mocking ve stubbing tekniklerini kullanmalısınız. Jest’in jest.mock() fonksiyonu ile tüm modülleri sahte versiyonlarıyla değiştirebilir, mockResolvedValue() veya mockRejectedValue() ile belirli fonksiyonların döneceği değerleri kontrol edebilirsiniz. Bu sayede testleriniz gerçek servislere bağımlı olmaz, daha hızlı ve güvenilir çalışır.
Soru 3: Testlerimi ne sıklıkla çalıştırmalıyım?
Cevap: Birim testleri geliştirme sürecinde mümkün olduğunca sık, hatta her kod değişikliğinde veya kaydettiğinizde çalıştırılmalıdır (Jest’in --watch modu bunun için idealdir). Entegrasyon testleri ve uçtan uca testler ise daha çok sürekli entegrasyon (CI) boru hattının (pipeline) bir parçası olarak, her kod birleştirmesinde veya dağıtımdan önce otomatik olarak çalıştırılmalıdır. Bu, hataların erken aşamada tespit edilmesini sağlar.
Soru 4: Ajan testlerinde performans nasıl ölçülür?
Cevap: Ajan testlerinde doğrudan performans ölçümü (örneğin yanıt süresi) genellikle birim veya entegrasyon testlerinin birincil odağı değildir. Bu tür ölçümler daha çok yük testleri (load testing) veya stres testleri (stress testing) ile yapılır. Ancak, testlerinizin kendisinin hızlı çalıştığından emin olmalısınız. Yavaş çalışan testler geliştirme sürecini aksatır. Jest, test sürelerini raporlar ve bu sayede yavaş testleri tespit edip optimize edebilirsiniz. Ajanın gerçek dünyadaki performansını ölçmek için ise Google Cloud Monitoring gibi araçlar kullanılmalıdır.
Soru 5: Test otomasyonunu CI/CD süreçlerine nasıl entegre edebilirim?
Cevap: Test otomasyonunu CI/CD süreçlerinize entegre etmek, kod kalitesini güvence altına almanın en etkili yoludur. Popüler CI/CD platformları (GitHub Actions, GitLab CI/CD, Jenkins, Google Cloud Build) ile kolayca entegrasyon sağlayabilirsiniz. Genellikle, her kod değişikliği bir depoya (repository) gönderildiğinde, CI/CD boru hattı otomatik olarak tetiklenir. Bu boru hattı, projenizin bağımlılıklarını kurar, tüm testleri (birim, entegrasyon, E2E) çalıştırır ve testler başarısız olursa dağıtımı durdurur. Testlerin başarılı olması durumunda ise kodun bir sonraki aşamaya (örneğin, test ortamına dağıtım) geçmesine izin verir. Bu, sürekli ve otomatik bir kalite kontrol döngüsü oluşturur.
#GoogleADK #TypeScript #AjanTesti #YazılımTesti #Jest #Mocking #EntegrasyonTesti #BirimTesti #Chatbot #YapayZeka