---
title: 'HTTP Testleri'
source: 'https://academia.sh/tr/kurslar/go-uretim/http-testleri'
course: 'Üretim Go: Test, Araçlar ve Başarım'
language: tr
updated: '2026-09-13T05:08:46+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Bir HTTP işleyicisini gerçek bir bağlantı noktası açmadan, sunucu ve istemci tarafı test yardımcılarıyla doğrudan çağırarak sınar'
    - 'Aynı işleyicinin üç isteğe verdiği kararı (200, 404, 400) koşum bilgisi olarak sınıflar ve kaynaktaki durum kodu sayısıyla karşılaştırır'
    - 'Sınanan isteğin gerçek bir bağlantıdan gelmediğini, isteğin adresini okuyarak sınırlar'
---

# HTTP Testleri

Aynı işleyici, gerçek bir bağlantı noktası hiç açılmadan üç istekle sınanıyor: var olan bir kalem 200, olmayan bir kalem 404, parametresiz bir istek 400 döndürüyor — üçü de yalnız isleyici.ServeHTTP çağrılarak öğrenilen bir koşum bilgisi. Sınırlayıcı ölçüm, isteğin adresinin RFC 5737'nin belge amaçlı ayırdığı sabit bir adres olduğunu okuyarak testin ağı değil işleyiciyi sınadığını gösteriyor.

Önceki ders bir tablonun vaka sayısıyla koşan alt test sayısının nasıl eşleştiğini ve bu
eşleşmenin kaynaktan mı koşumdan mı geldiğini ölçtü. Bu ders aynı soruyu ağ katmanına
taşıyor: bir sunucunun bir isteğe verdiği yanıt da yalnız koşturularak öğrenilebilen bir
bilgi — ama "koşturmak" burada gerçek bir bağlantı noktası açmak, bir soket dinlemek, bir
istemcinin o soketi çağırması anlamına gelmiyor. Test paketinin `net/http/httptest` alt
paketi bu ikisini ayırıyor: bir işleyicinin davranışını sınamak, o işleyiciyi ağın
üzerinden değil, doğrudan bir işlev çağrısıyla sınamak anlamına gelebiliyor.

Üretim ortamında çalışan bir HTTP sunucusunun davranışı, doğrudan sunucuya bağlanıp
isteği elle göndermeden sınanabiliyorsa, bu sınama sunucunun çalıştığı makinenin ağ
ayarlarına, boş bir bağlantı noktası bulunmasına ya da bir zaman aşımına **bağlı
kalmıyor** — yani sonucu ortamdan bağımsız oluyor. Bu ders bu iddiayı iki adımda ölçüyor: önce bir
işleyicinin gerçekten bir bağlantı olmadan nasıl çağrıldığı, sonra bu çağrının ürettiği
isteğin gerçek bir ağdan gelmediğinin kendisinin nasıl doğrulanabildiği.

## İşleyici Doğrudan Çağrılıyor, Bağlantı Noktası Hiç Açılmıyor

`envanter` paketi bu derste HTTP katmanına taşınıyor: `kalemIsleyici`, bir depo
(`ad`'dan `Kalem`'e bir sözlük) alıp bir `http.HandlerFunc` döndürüyor. İşleyici üç
adımda karar veriyor: önce isteğin sorgu dizgisinden `ad` parametresini okuyor, sonra bu adın depoda
olup olmadığına bakıyor, son olarak varsa kalemin fiyatını düz metin olarak yazıyor.
İstek bir `ad` sorgu parametresi taşımıyorsa `400`, taşıdığı ad depoda yoksa `404`, varsa
kalemin fiyatını gövdede döndürüp `200` veriyor. Bu işleyicinin sıradan bir Go HTTP
sunucusuna bağlanabilmesi için tek gereken, onu bir `http.ServeMux`e kaydetmek — ama bu
ders onu hiç sunucuya bağlamıyor.

İşleyicinin imzası da bu ayrımı kolaylaştıran bir seçim: `kalemIsleyici` depoyu bir
parametre olarak alıyor ve bir `http.HandlerFunc` döndürüyor, deponun kendisi paket
düzeyinde bir değişken olarak sabitlenmiyor. Bu, aynı işleyicinin farklı bir depoyla
— örneğin bu derste kullanılan tek kalemlik sabit depoyla, ya da başka bir ölçümde boş
bir depoyla — yeniden kurulabilmesini sağlıyor; işleyicinin kendisi hangi depoyla
çalıştığını hiç bilmiyor, yalnız kendisine verilen `map[string]Kalem` değerine bakıyor.

Bu yapı bir kapanış (closure) — `kalemIsleyici`'nin döndürdüğü işlev, kendisini
oluşturan çağrıda verilen `depo` değişkenini gövdesinde tutmaya devam ediyor, tıpkı bu
konudan önceki dersin `orneklerVer` yardımcısının kendi çağıranına bağlı kalması gibi.
Test yardımcıları bu kapanışın içine hiç girmiyor; yalnız dışarıdan görünen
`http.HandlerFunc` imzasını çağırıyor ve kapanışın içinde hangi deponun tutulduğu ölçümün
kendisi için görünmez kalıyor.

İsteği kuran çağrı `http.MethodGet` sabitini kullanıyor, `"GET"` dizgisini elle
yazmak yerine — standart kitaplığın kendi tanımladığı bu sabitler, bir yöntem adının
yanlış yazılmasını derleme zamanında değil ama en azından tek bir kaynaktan yakalıyor
ve bu ders boyunca kullanılan bütün istekler aynı sabiti paylaşıyor.

`httptest` paketinin iki yordamı bu bağlantıyı gereksiz kılıyor: `httptest.NewRequest`,
bir istemcinin ağdan göndereceği bir isteğin Go karşılığını üretiyor — istemci tarafı bir
yardımcı, çünkü normalde bu isteği bir istemci bir bağlantı üzerinden yollar.
`httptest.NewRecorder` ise bir yanıtı toplayan, bir `http.ResponseWriter` gerçekleştiren bir
yapı — sunucu tarafı bir yardımcı, çünkü normalde bir yanıtı bir bağlantının karşı ucuna
yazan şey budur. İkisi birlikte kullanıldığında, `isleyici.ServeHTTP(kayit, istek)`
çağrısı işleyiciyi gerçek bir ağ yığıtına hiç dokunmadan, aynı süreç içinde, bir işlev
çağrısı gibi çalıştırıyor.

`httptest` paketinin bir de `NewServer` yordamı var ve o gerçekten bir bağlantı noktası
açıyor — verilen işleyiciyi yerel makinede geçici, boşta bulunan bir bağlantı noktasına
bağlıyor, bir adres döndürüyor ve o adrese gerçek bir istemci isteği gönderilmesini
bekliyor. Bu yordam gerçek bir ağ yolunu (bağlantı kurma, veri gönderme, bağlantıyı
kapatma) sınamak istendiğinde gerekli, ama bu derste kullanılmıyor: `kalemIsleyici`'nin
mantığını sınamak için ağ yoluna hiç ihtiyaç yok ve gerçek bir bağlantı noktası açmak
hem ölçümü yavaşlatır hem de hangi bağlantı noktasının boş olduğuna bağlı, koşumdan
koşuma değişebilecek bir bilgi ekler — tam olarak bu konunun yazılmayan yasağı. `Recorder`
ve `NewRequest` ikilisi bu maliyeti hiç doğurmadan aynı mantığı sınıyor.

## Üç İsteğin Kararı

**TS7.** İşleyicinin kaynağında kaç ayrı durum kodu yazılı olduğu kaynaktan sayılabiliyor:
`http.StatusBadRequest` ve `http.StatusNotFound` açıkça yazılı, üçüncü durum (`http.StatusOK`,
hiçbir durum kodu yazılmadığında `net/http`'nin kendi varsayımı) hiçbir sabitle anılmadan örtük
duruyor — toplam üç ayrı sonuç kaynaktan çıkarılabiliyor. Hangi isteğin **hangi** sonucu
tetikleyeceği ise kaynaktan değil, işleyiciyi gerçekten çağırarak öğreniliyor.

`durumKoduSayisiKaynakta` bu sayımı gerçekten kod bloğunu bir araç ya da bir çalıştırma
kullanmadan yapıyor: işleyicinin gövdesini bir dizgi olarak alıp içinde `StatusBadRequest`
ve `StatusNotFound` sabitlerinin geçip geçmediğine bakıyor, `StatusOK`'in kendisi hiç
yazılmadığı için onu örtük bir varsayım olarak sayıyor. Bu, önceki iki derste kurulan
kaynak sayımı yordamının (`func Test` geçişlerini saymak, `{"` ile başlayan satırları
saymak) bir başka biçimi — burada sayılan şey bir işlev adı ya da bir vaka satırı değil,
bir sabitin adı, ama yöntem aynı: kaynağı bir dizgi olarak okuyup içinde aranan bir
kalıbın kaç kez geçtiğini saymak.

```go
// olcum.go — ayni isleyici uc istekle sinaniyor, gercek baglanti noktasi acilmiyor
package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
)

type Kalem struct {
	Ad    string
	Adet  int
	Fiyat int
}

func kalemIsleyici(depo map[string]Kalem) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ad := r.URL.Query().Get("ad")
		if ad == "" {
			http.Error(w, "ad gerekli", http.StatusBadRequest)
			return
		}
		k, bulundu := depo[ad]
		if !bulundu {
			http.Error(w, "bulunamadi", http.StatusNotFound)
			return
		}
		fmt.Fprintf(w, "%d", k.Fiyat)
	}
}

const isleyiciKaynagi = `
		ad := r.URL.Query().Get("ad")
		if ad == "" {
			http.Error(w, "ad gerekli", http.StatusBadRequest)
			return
		}
		k, bulundu := depo[ad]
		if !bulundu {
			http.Error(w, "bulunamadi", http.StatusNotFound)
			return
		}
		fmt.Fprintf(w, "%d", k.Fiyat)
`

func durumKoduSayisiKaynakta(kod string) int {
	n := 1 // yazili sabiti olmayan varsayilan basarili donus
	for _, s := range []string{"StatusBadRequest", "StatusNotFound"} {
		if strings.Contains(kod, s) {
			n++
		}
	}
	return n
}

func main() {
	fmt.Println("-- kaynak: kac ayri durum kodu yazili --")
	fmt.Println("isleyicide yazili durum kodu sayisi:", durumKoduSayisiKaynakta(isleyiciKaynagi))

	depo := map[string]Kalem{"kalem": {"kalem", 3, 250}}
	isleyici := kalemIsleyici(depo)

	fmt.Println()
	fmt.Println("-- kosum: uc istegin karari --")
	istek1 := httptest.NewRequest(http.MethodGet, "/kalem?ad=kalem", nil)
	kayit1 := httptest.NewRecorder()
	isleyici.ServeHTTP(kayit1, istek1)
	fmt.Println("var olan kalem, durum kodu:", kayit1.Code, "govde:", kayit1.Body.String())

	istek2 := httptest.NewRequest(http.MethodGet, "/kalem?ad=olmayan", nil)
	kayit2 := httptest.NewRecorder()
	isleyici.ServeHTTP(kayit2, istek2)
	fmt.Println("olmayan kalem, durum kodu:", kayit2.Code)

	istek3 := httptest.NewRequest(http.MethodGet, "/kalem", nil)
	kayit3 := httptest.NewRecorder()
	isleyici.ServeHTTP(kayit3, istek3)
	fmt.Println("parametresiz istek, durum kodu:", kayit3.Code)
}
```

```
-- kaynak: kac ayri durum kodu yazili --
isleyicide yazili durum kodu sayisi: 3

-- kosum: uc istegin karari --
var olan kalem, durum kodu: 200 govde: 250
olmayan kalem, durum kodu: 404
parametresiz istek, durum kodu: 400
```

**TS8.** Kaynakta üç ayrı durum kodu yazılı olduğu bilinse de, hangi isteğin hangi kodu
tetikleyeceği ancak `ServeHTTP` çağrıldıktan sonra okunuyor — üç `httptest.NewRequest` çağrısı
birer istemci isteği kuruyor; üç `httptest.NewRecorder` bu isteklerin yanıtlarını ayrı ayrı
topluyor ve `kayit.Code` her seferinde işleyicinin fiilen yazdığı durum kodunu döndürüyor. Bu,
önceki iki derste kurulan aynı ayrımın ağ katmanındaki karşılığı: kaç sonuç **olabileceği**
kaynaktan sayılabiliyor, hangi sonucun **gerçekleştiği** yalnız koşumdan okunabiliyor. Test Paketi
Temelleri'nde bu ayrım bir işlevin geçip geçmediğiydi, Tablo Güdümlü Testler'de bir vakanın kaç
alt teste dönüştüğüydü; burada aynı ayrım bir isteğin hangi durum koduna dönüştüğü sorusuna
uygulanıyor — üç ders de aynı iskeleti farklı bir yüzeyde koşturuyor.

Üç isteğin de aynı `isleyici` değişkeni üzerinden geçmesi önemli — işleyici bir kez
kuruluyor, sonra üç ayrı girdiyle üç kez çağrılıyor, tıpkı bir sunucunun gerçek
çalışmasında aynı işleyicinin ardışık isteklere yanıt vermesi gibi. Aradaki fark, burada
bu ardışık çağrıların bir dinleyici soketten değil, doğrudan bir döngüden geldiği.

İlk isteğin gövdesi de ayrıca okunuyor: `kayit1.Body.String()` yalnız durum kodunu değil,
işleyicinin `fmt.Fprintf` ile yazdığı `250` dizgisini de döndürüyor — önceki derslerdeki
`envanter` paketinin kuruş cinsinden fiyat kararı burada da aynen sürüyor. Diğer iki
istekte gövde okunmuyor, çünkü `http.Error` çağrısının yazdığı metin bu ölçümün sorusuyla
ilgisiz; sorulan tek şey durum kodu ve `http.Error`'ın ürettiği metin sürüme bağlı,
kaynakta yazılı olmayan bir ayrıntı. Bu, ikinci ve üçüncü ölçümde araç çıktısının
kendisini basmama kuralının doğal bir uzantısı: yalnız sorulan ölçü okunuyor, geri kalan
her şey görmezden geliniyor.

## Sınırlayıcı Ölçüm: Sınanan Şey Ağ Değil İşleyici

**TS9.** `httptest.NewRequest`'in ürettiği isteğin `RemoteAddr` alanı hep aynı sabit
değeri taşıyor: `192.0.2.1:1234`. Bu adres rastgele değil — RFC 5737'nin belgeleme ve
örnek amaçlı ayırdığı, hiçbir gerçek ağda yönlendirilmeyen bir blok. `httptest` paketi
bu adresi her çağrıda aynen kullanıyor, çünkü ürettiği istek hiçbir zaman gerçek bir
bağlantıdan gelmiyor; bir bağlantı hiç kurulmadığı için gerçek bir uzak adres de yok.

Gerçek bir HTTP sunucusunda `RemoteAddr` alanı, bağlantıyı açan istemcinin gerçek
adresini taşır ve her istekte, her koşumda değişebilir — tam olarak bu konunun
başından beri basılmaması söylenen türden bir bilgi. `httptest.NewRequest`'in bu alana
sabit bir değer koyması, aracın kendi tasarımının bir sonucu: bir istek gerçek bir
bağlantıdan gelmediği için, dolduracak gerçek bir adres de yok ve paket bu boşluğu
rastgele ya da boş bırakmak yerine, hiçbir gerçek ağda karşılığı olmayan sabit bir
değerle dolduruyor. Bu seçim, bir işleyicinin `RemoteAddr` alanına bakan bir mantığı
(örneğin bir erişim kısıtlaması) varsa, bu mantığın `httptest` altında hep aynı sabit
adresle karşılaşacağını da ima ediyor — bu ölçümde `kalemIsleyici`'nin böyle bir mantığı
yok, ama bu sınır ileride benzer bir işleyici sınanırken hatırlanması gereken bir
ayrıntı — bir erişim kısıtlaması gerçek bir dağıtımda doğru çalışsa bile, bu test
yardımcısıyla yazılmış bir sınama o kısıtlamayı hiçbir zaman gerçekten sınamaz, çünkü
sınamanın kendisi sabit bir adresle çalışır.

```go
// sinir.go — istegin adresi belge amacli ayrilmis sabit bir blok, gercek baglanti degil
package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
)

func main() {
	istek := httptest.NewRequest(http.MethodGet, "/kalem?ad=kalem", nil)
	belgeAmacliMi := strings.HasPrefix(istek.RemoteAddr, "192.0.2.")
	fmt.Println("istegin adresi belge amacli ayrilmis bir blokta mi:", belgeAmacliMi)
}
```

```
istegin adresi belge amacli ayrilmis bir blokta mi: true
```

Bu derste kullanılan test yardımcısı, dolayısıyla, işleyicinin **mantığını** sınıyor —
girdiye göre doğru durum kodunu üretip üretmediğini — ağ katmanının kendisini değil.
Gerçek bir ağ bağlantısı burada hiç kurulmadı, hiç dinlenmedi, hiç kapatılmadı; sınama
tamamen aynı sürecin içinde, bir işlev çağrısı zincirinde kaldı. Bu bir eksiklik değil,
bilinçli bir sınır: bir işleyicinin mantığını sınamak için gerçek bir soket açmak
gereksiz bir maliyet ve gereksiz bir kırılganlık kaynağı — bir bağlantı noktasının
başka bir süreç tarafından kullanılıyor olması gibi, işleyicinin kendi mantığıyla hiç
ilgisi olmayan bir başarısızlık nedeni ekliyor. Ama bu sınırın bir bedeli de var: bu
test, gerçek bir dinleyicinin kurulumunda, zaman aşımlarında ya da eşzamanlı bağlantı
yönetiminde çıkabilecek bir kusuru hiç yakalayamıyor, çünkü o katman bu ölçümde hiç
çalışmadı.

Bu ayrım, işleyicinin kendi imzasında da görünür duruyor: `http.HandlerFunc`, bir
`http.ResponseWriter` ile bir `*http.Request` alan sıradan bir Go işlevi — ne bir soket
tanıtıcısı alıyor ne de bir ağ bağlamı. İşleyicinin gördüğü tek şey bu iki değer ve test
yardımcıları da tam olarak bu iki değeri, gerçek bir bağlantı kurmadan üretmenin bir
yolu. Bu yüzden bir işleyicinin ağdan mı yoksa `httptest`'ten mi çağrıldığını, işleyicinin
kendi gövdesinden anlamak mümkün değil — işleyici ikisi arasındaki farkı hiç görmüyor,
yalnız kendisine verilen `http.ResponseWriter` ve `*http.Request` çiftiyle çalışıyor. Bu,
işleyicinin test edilebilirliğinin kaynağı: arayüzün kendisi (bu iki tip) sabit kaldığı
sürece, arkasındaki gerçekleştirim (gerçek bir bağlantı mı, bir kayıt yapısı mı)
işleyicinin gözünde fark etmiyor. Sıradaki ders aynı gözlemi bir adım öteye taşıyor —
işleyicinin kendisi değil, işleyicinin **çağırdığı** bir bağımlılık bu kez arayüzün
arkasına alınıyor.

## Özet

- `httptest.NewRequest` istemci tarafı, `httptest.NewRecorder` sunucu tarafı bir yardımcı;
  ikisi birlikte bir işleyiciyi gerçek bir bağlantı noktası açmadan, doğrudan bir işlev
  çağrısıyla sınıyor.
- İşleyicideki durum kodu sayısı (üç) kaynaktan sayılabiliyor, ama hangi isteğin hangi
  kodu tetikleyeceği yalnız `ServeHTTP` çağrılarak öğrenilebiliyor.
- Aynı `isleyici` değişkeni üç farklı istekle art arda çağrıldı; üçünün de kararı bir
  koşum bilgisi olarak okundu, adres ve bağlantı noktası hiçbir yerde basılmadı.
- Sınırlayıcı ölçüm: `httptest.NewRequest`'in ürettiği isteğin adresi RFC 5737'nin belge
  amaçlı ayırdığı sabit bir blokta — bu, sınanan şeyin ağ değil işleyicinin kendisi
  olduğunun kanıtı.
- Bu sınır bir eksiklik değil bilinçli bir seçim, ama bedeli de var: gerçek bir
  dinleyicinin davranışına bağlı kusurlar bu testte hiç ortaya çıkmıyor.
- `httptest.NewServer` gerçek bir bağlantı noktası açabiliyor, ama bu ders onu kullanmadı;
  işleyicinin mantığını sınamak için gerçek bir ağ yoluna hiç ihtiyaç olmadığı için bu
  maliyet baştan dışlandı.

## Sonraki Adım

Bu derste bir işleyicinin davranışının gerçek bir bağlantı hiç kurulmadan, doğrudan
çağrılarak sınandığı ölçüldü: kaynakta kaç ayrı sonuç yazılı olduğu sayılabildi, hangi
isteğin hangi sonucu doğurduğu yalnız koşarak öğrenildi ve isteğin kendisinin gerçek bir
ağdan gelmediği isteğin adresi okunarak doğrulandı. Ama işleyicinin kendisi hâlâ, `main`
işlevinin içinde elle kurulan sabit bir sözlük kullanıyordu — gerçek bir üretim
programında bu veri bir dış bağımlılıktan, örneğin bir depolama katmanından geliyor
olurdu ve o katman her sınamada gerçekten çalıştırılamayacak kadar yavaş ya da
kararsız olabilirdi. Sıradaki ders bu bağımlılığı bir arayüzün arkasına alıyor ve aynı
test gövdesinin gerçek ve sahte bir gerçekleştirimle nasıl koştuğunu ölçüyor — sahteyi
mümkün kılan şeyin, arayüzün kaynakta yazılı olması olduğunu göstererek.
