---
title: 'Sahte Nesneler'
source: 'https://academia.sh/tr/kurslar/python-projeleri/sahte-nesneler'
course: 'Python Projeleri: Paketleme ve Test'
language: tr
updated: '2026-08-17T18:10:32+00:00'
license: 'CC BY-SA 4.0'
---

# Sahte Nesneler

Dış kaynağa uzanan aynı test üç kipte 3 ayrı sonuç verir, sahte nesneyle 1'e iner; ama depodan sapmış bir sahte de 1 ayrı sonuç verir ve beklenti onun yanında yazıldığı için geçer, gerçek kaynakla arasında 2 ayrı sonuç durur ve sözleşme sınaması sapmayı tek pakette bulur.

Önceki ders düzeneğin paylaşılan durumu her testte yeniden kurarak ayrı karar sayısını
ikiden bire indirdiğini ölçtü. Bunu yapabildi, çünkü kurduğu şey kendi elindeydi:
bellekteki bir nesne, bir sayaç. Yeniden kurmak bir atama kadar ucuzdu ve sonucu tam
olarak belirliyordu.

Testin dokunduğu her şey böyle değildir. Bir çözümleyici, kabul edilen sürümleri öğrenmek
için bir **paket deposuna** sorar; depo bir ağın öbür ucundadır, içeriği bizim
denetimimizde değildir ve aynı soruya her zaman aynı yanıtı vermez. Bu dersin sorusu
şudur: **böyle bir kaynağa uzanan test kaç ayrı sonuç verir, ve yerine bir sahte nesne
konduğunda bu sayı neye iner?**

## Dış Kaynağın Üç Kipi

Ölçümdeki dış kaynak bir **depo istemcisidir** ve ders içinde modellenmiştir; hiçbir ürün
adı geçmez, gerçek bir istek yapılmaz. İstemcinin tek yöntemi vardır: bir paketin
yayımlanmış sürümlerini döndürür. Aynı çağrı üç ayrı kipte üç ayrı şey yapar.

**Tam.** Depo bilinen bütün sürümleri döndürür. Çözümleme ortak tanımın sonucunu verir.

**Gecikmiş yansıma.** Deponun bir kopyası henüz güncellenmemiştir ve en son sürümü
taşımaz. İstek başarılıdır, yanıt geçerlidir, ama eksiktir — ve eksiklik yanıtın içinde
görünmez.

**Kesinti.** Depoya erişilemez ve çağrı bir istisna fırlatır. Çözümleme hiç sonuç
üretmez.

Üç kip de gerçek bir kurulumda görülür ve hiçbiri bir kusur değildir; deponun normal
işleyişinin parçasıdır. Testin bakış açısından ise üçü tek bir şeydir: **aynı test, üç
ayrı sonuç.**

Üç sayısı ölçümün seçimidir, kaynağın sınırı değil. Gerçek bir depoda kip sayısı daha
büyüktür: yeni bir sürüm yayımlanır ve liste uzar, bir sürüm geri çekilir ve liste
kısalır, istek sınırlanır ve yanıt gecikir. Bu dersin ölçtüğü sayının anlamı "kaynak tam
üç şey yapar" değil, **kaynağın kip sayısı ne olursa olsun testin sonuç sayısı ona eşit
olur** demektir. Test dış kaynağa bağlıysa, kaynağın her ayrı davranışı testin bir ayrı
sonucudur.

## Sahtenin Konabileceği Yer

Bir sahte nesnenin gerçeğin yerine geçebilmesi bir tasarım koşuluna bağlıdır: gerçeğin
**dışarıdan verilmiş** olması. Ölçümdeki çözümleme işlevi sürüm listesini kendi içinde
üretmiyor; kendisine verilen istemciden istiyor. Bu yüzden istemcinin yerine başka bir
nesne koymak bir argüman değiştirmekten ibaret.

Aynı işlev depoya doğrudan kendisi sorsaydı — istemciyi kendi içinde kurup kullansaydı —
sahte nesnenin konabileceği bir yer kalmazdı. O durumda testin elinde iki seçenek olurdu:
gerçek depoya sormak ve üç kipe açık kalmak, ya da çalışma zamanında adı yeniden bağlayıp
işlevin gördüğü nesneyi değiştirmek. İkincisi işe yarar ama testi işlevin **iç
gerçekleştirimine** bağlar: işlev istemciyi başka bir adla kurmaya başladığında test,
davranış hiç değişmeden düşer.

Bağımlılığın dışarıdan verilmesi Yazılım Kalitesi ve Test müfredatının Birim Testi ve Test
Odaklı Geliştirme kursunda "test edilebilir tasarım" olarak kurulmuştu; burada
tekrarlanmıyor. Kaydedilecek tek şey şu: **sahte nesnenin ayrı sonuç sayısını
indirebilmesi, kodun onu kabul edecek biçimde yazılmış olmasına bağlıdır.**

## Sahte Nesne Neyi Değiştirir

**Sahte nesne (mock / stub)**, gerçek bir bağımlılığın yerine konan ve aynı çağrılara
yanıt veren bir nesnedir. Yanıtı testin kendisi belirler; ağ yoktur, gecikme yoktur,
kesinti yoktur. Bunun ölçüme etkisi doğrudandır: kaynağın kip sayısı kaç olursa olsun,
sahte nesne tek bir yanıt verir ve test **1 ayrı sonuç** üretir.

Sahte nesnenin türleri ve hangi durumda hangisinin kullanılacağı Yazılım Kalitesi ve Test
müfredatının Birim Testi ve Test Odaklı Geliştirme kursunda kurulmuştur; **yordam burada
tekrarlanmıyor**, atıf yapılıyor. Bu derste ölçülen tek şey **yalıtımın kaç ayrı sonucu
tekleştirdiğidir.**

Ölçümdeki sahte nesne iki iş yapar. Birincisi, yanıtı sabitler: kendisine verilen sürüm
sözlüğünü döndürür. İkincisi, aldığı çağrıları kaydeder — kaç kez ve hangi paket için
çağrıldığını saklar. Bu ikinci iş, testin yalnız dönen değeri değil, **yapılan çağrıyı**
da sınamasına izin verir; ölçümün son satırı bu kaydı yazıyor.

## Yalıtımın Ödettiği

Sahte nesne testi kaynağın kiplerinden kurtarır, ama bir bedel karşılığında: test artık
**gerçek kaynağa dokunmuyor.** Sabitlenen yanıtın gerçek deponun verdiği yanıtla aynı
olduğunu söyleyen hiçbir şey kalmaz. Sahte nesne bir kez yazılır, depo ise değişmeye
devam eder.

Buna **sözleşme sapması** denir ve ölçümün ikinci bölümü onu sayıyor. Sahtenin sürüm
listesine gerçek depoda bulunmayan bir sürüm eklendiğinde test **yine geçer** — çünkü
testin beklentisi sahtenin yanında, sahteye bakılarak yazılmıştır. Beklenti ile sahte
birbirini doğrular ve ikisi birlikte gerçekten uzaklaşır.

Sapmanın iki yönü de olur ve ikisi de sessizdir. Sahte, gerçeğin artık vermediği bir yanıtı
vermeye devam edebilir; ya da gerçek, sahtenin hiç öngörmediği bir yanıt vermeye
başlayabilir. Birinci durumda test yeşil kalır ve kurulum kırılır; ikinci durumda test
yine yeşil kalır ve kurulum, sahtenin hiç denemediği bir yolu izler.

Bu sapmayı görünür kılan düzeneğe **sözleşme sınaması** denir: aynı çağrılar hem sahteye
hem gerçek kaynağa yapılır ve yanıtlar karşılaştırılır. Böyle bir sınama yalıtımı bozar —
gerçek kaynağa dokunur, dolayısıyla kaynağın üç kipine yeniden açıktır. Bu yüzden birim
testlerin yanında değil, ayrı ve seyrek koşan bir küme olarak tutulur.

## Ölçümün Varsayımları

- **PT19** — Depo istemcisi ders içinde modellenir; gerçek bir ağ isteği yapılmaz, gerçek
  paket indirilmez, hiçbir ürün adı yazılmaz.
- **PT20** — Kâhin ortak tanımın deposudur: hangi paketin hangi sürümleri taşıdığını biz
  yazdığımız için biliyoruz. `DEPO` ve `BILDIRIM` değiştirilmez.
- **PT21** — İstemcinin üç kipi vardır: tam, gecikmiş yansıma ve kesinti. Gecikmiş
  yansıma her paketin **en son sürümünü** düşürür; kesinti bir istisna fırlatır.
- **PT22** — Çözümleme "en yeni" stratejisiyle yapılır ve sürüm listesini yalnız
  istemciden alır; başka hiçbir kaynağa bakmaz.
- **PT23** — Kesinti sonucu "sonuç yok" olarak yazılır ve ayrı bir sonuç sayılır; hata
  da bir sonuçtur.
- **PT24** — Sahte nesne yanıtı sabitler ve çağrıları kaydeder. Kip kavramı sahtede
  yoktur; üç koşumda da aynı yanıtı verir.
- **PT25** — Sapmış sahte, `olcum` paketine gerçek depoda bulunmayan bir sürüm ekler.
  Öbür iki paket dokunulmadan kalır.
- **PT26** — Testin beklentisi, kullanılan sahtenin yanında ve ona bakılarak yazılır. Bu,
  sahte nesneyle çalışan bir takımda olağan yazım biçimidir.
- **PT27** — Sözleşme sınaması aynı üç çağrıyı iki kaynağa sorar ve yanıtları paket paket
  karşılaştırır; sapma bulunan paket sayısını verir.
- **PT28** — Süre ölçülmez. Sayılan şey ayrı sonuç, sapma bulunan paket ve sahteye giden
  çağrı sayısıdır.

## Ölçüm

```python
"""Sahte nesne: dis kaynagin uc yaniti ile tek yanit. Istemci ders icinde modellenir.

Bolum 1 - gercek istemci uc kipte, sahte nesne tek kipte.
Bolum 2 - sahte nesne depodan sapinca ne oluyor.
"""

DEPO = {"olcum": [(1, 0), (1, 1), (1, 2), (2, 0)],
        "rapor": [(0, 9), (1, 0), (1, 1)],
        "ortak": [(3, 0), (3, 1), (3, 2), (4, 0)]}
BILDIRIM = {"olcum": ((1, 0), (2, 0)), "rapor": ((1, 0), (2, 0)),
            "ortak": ((3, 0), (4, 0))}


class Kesinti(Exception):
    """Dis kaynaga erisilemedigini bildirir."""


class GercekIstemci:
    """Dis kaynak: ayni cagri kipe gore ayri yanit verir."""

    def __init__(self, kip):
        self.kip = kip
        self.cagri = 0

    def surumler(self, paket):
        self.cagri += 1
        if self.kip == "kesinti":
            raise Kesinti(paket)
        if self.kip == "gecikmiş yansıma":
            return DEPO[paket][:-1]
        return list(DEPO[paket])


class SahteIstemci:
    """Yanitı sabitleyen sahte nesne; cagriyi da sayar."""

    def __init__(self, kaynak=None):
        self.kaynak = kaynak if kaynak is not None else DEPO
        self.cagri = 0
        self.istenen = []

    def surumler(self, paket):
        self.cagri += 1
        self.istenen.append(paket)
        return list(self.kaynak[paket])


def coz(istemci, strateji="en yeni"):
    """Ayni bildirimi cozer; surum listesini istemciden alir."""
    secim = {}
    for paket in sorted(BILDIRIM):
        alt, ust = BILDIRIM[paket]
        adaylar = [s for s in istemci.surumler(paket) if alt <= s < ust]
        secim[paket] = adaylar[-1] if strateji == "en yeni" else adaylar[0]
    return secim


def metin(secim):
    return " ".join(".".join(map(str, secim[p])) for p in ("olcum", "ortak", "rapor"))


def dene(istemci):
    try:
        return metin(coz(istemci))
    except Kesinti:
        return "sonuç yok"


KIPLER = ("tam", "gecikmiş yansıma", "kesinti")

print(f"{'koşum':<22s} {'gerçek istemci':>16s} {'sahte nesne':>16s}")
gercek, sahte = [], []
for kip in KIPLER:
    g, s = dene(GercekIstemci(kip)), dene(SahteIstemci())
    gercek.append(g)
    sahte.append(s)
    print(f"{'kip: ' + kip:<22s} {g:>16s} {s:>16s}")
print(f"aynı test, gerçek kaynakla {len(set(gercek))} ayrı sonuç; "
      f"sahte nesneyle {len(set(sahte))}")

print()
KAYAN = dict(DEPO, olcum=DEPO["olcum"] + [(1, 3)])
kayan_sahte = SahteIstemci(KAYAN)
kayan = dene(kayan_sahte)          # beklenti sahtenin yanindan yazilir
print(f"{'koşum':<31s} {'sonuç':>12s} {'beklenti':>12s} {'karar':>7s}")
for ad, sonuc, beklenti in (("sahte, depoyla aynı", dene(SahteIstemci()),
                             dene(SahteIstemci())),
                            ("sahte, depodan sapmış", kayan, kayan),
                            ("gerçek kaynak, sapmış beklenti",
                             dene(GercekIstemci("tam")), kayan)):
    print(f"{ad:<31s} {sonuc:>12s} {beklenti:>12s} "
          f"{('geçer' if sonuc == beklenti else 'düşer'):>7s}")
print(f"sapmış sahte ile gerçek kaynak arasında "
      f"{len({kayan, dene(GercekIstemci('tam'))})} ayrı sonuç var")

sozlesme = [p for p in sorted(BILDIRIM)
            if SahteIstemci(KAYAN).surumler(p) != GercekIstemci("tam").surumler(p)]
print(f"sözleşme sınaması aynı üç çağrıyı iki kaynağa sorar ve "
      f"{len(sozlesme)} pakette sapma bulur: {', '.join(sozlesme)}")
print(f"sahte nesne {kayan_sahte.cagri} çağrı aldı, istenen paketler "
      f"{', '.join(kayan_sahte.istenen)}")
```

```
koşum                    gerçek istemci      sahte nesne
kip: tam                    1.2 3.2 1.1      1.2 3.2 1.1
kip: gecikmiş yansıma       1.2 3.2 1.0      1.2 3.2 1.1
kip: kesinti                  sonuç yok      1.2 3.2 1.1
aynı test, gerçek kaynakla 3 ayrı sonuç; sahte nesneyle 1

koşum                                  sonuç     beklenti   karar
sahte, depoyla aynı              1.2 3.2 1.1  1.2 3.2 1.1   geçer
sahte, depodan sapmış            1.3 3.2 1.1  1.3 3.2 1.1   geçer
gerçek kaynak, sapmış beklenti   1.2 3.2 1.1  1.3 3.2 1.1   düşer
sapmış sahte ile gerçek kaynak arasında 2 ayrı sonuç var
sözleşme sınaması aynı üç çağrıyı iki kaynağa sorar ve 1 pakette sapma bulur: olcum
sahte nesne 3 çağrı aldı, istenen paketler olcum, ortak, rapor
```

## Üçten Bire

Üst tablo yalıtımın ödediğini veriyor. Gerçek istemciyle aynı test **3 ayrı sonuç**
üretiyor: tam kipte `1.2 3.2 1.1`, gecikmiş yansımada `1.2 3.2 1.0`, kesintide sonuç yok.
Sahte nesneyle üç koşumun üçü de `1.2 3.2 1.1` veriyor — **1 ayrı sonuç.**

Ortadaki satır bu üçlünün en tehlikelisidir ve ayrıca bakılmayı hak eder. Kesinti kendini
duyurur: bir istisna fırlar, koşum kırmızı olur ve nedeni ortadadır. Gecikmiş yansıma
hiçbir şey duyurmaz. İstek başarılıdır, yanıt geçerli bir listedir, çözümleme sorunsuz
tamamlanır ve **yalnız `rapor` paketinin sürümü değişir.** Böyle bir farkı yakalayan tek
şey, beklentinin tam olarak yazılmış olmasıdır — testin "bir sonuç döndü" demesi yetmez.

Sayının üçten bire inmesinin anlamı şudur: sahte nesne testi **kaynağın kiplerinden**
kurtardı. Test artık deponun gecikmesine, kesintisine ve içeriğinin değişmesine karşı
duyarsız. Kurtardığı şey budur ve azımsanacak bir şey değildir; sıra bağımlılığı gibi,
kaynak kipi de kararı kodun dışından değiştiren bir etkendir.

## Tekleşen Sonucun Doğruluğu

Alt tablo sahte nesnenin **kurtaramadığını** veriyor ve önceki dersin uyarısını başka bir
yüzeyde tekrarlıyor.

İlk iki satır aynı kararı veriyor: geçer. İkisinin de sahtesi tek bir yanıt döndürüyor,
ikisi de **1 ayrı sonuç** üretiyor. Ama biri deponun içeriğini taşıyor, öbürü depoda
olmayan bir sürüm uyduruyor. Testin geçmesinin nedeni ikisinde de aynı: **beklenti,
sahteye bakılarak yazıldı.** Sahte ne derse beklenti onu yazar ve karşılaştırma
kendiliğinden tutar.

Üçüncü satır ise ancak biri gerçek kaynağı çağırırsa var olur. Sapmış beklenti gerçek
kaynağa sorulduğunda düşüyor, çünkü aralarında **2 ayrı sonuç** duruyor. Bu iki sonuç
takımın hiçbir koşumunda karşı karşıya gelmiyor; birim testler sahteyle koşuyor, gerçek
kaynak yalnız kurulum sırasında devreye giriyor.

Sözleşme sınaması tam bu boşluğu kapatıyor: aynı üç çağrıyı iki kaynağa soruyor ve
**1 pakette** sapma buluyor — `olcum`. Sapmanın tek pakette olması ölçümün ölçeğini de
veriyor: üç paketin ikisinde sahte hâlâ doğru, biri kaymış. Sözleşme sınaması olmadan bu
oran hiç bilinmez, çünkü bilinmesi için gerçek kaynağa sorulması gerekir.

Son satır sahte nesnenin ikinci işini gösteriyor. Sahte **3 çağrı** almış ve üç paketi de
sormuş. Bu kayıt, dönen değerin ötesinde bir şey sınamayı sağlar: çözümleyici gerçekten
her paket için deposuna sordu mu, yoksa bir kısmını önbellekten mi aldı? Dönen değer bu
sorunun yanıtını taşımaz, çağrı kaydı taşır.

Çağrı kaydının kendi bedeli de vardır. Çağrı sayısını sınayan bir test, çözümleyicinin
**nasıl çalıştığına** bağlanır: bir gün önbellek eklenir, çağrı sayısı üçten bire iner ve
test düşer — oysa çözümleme aynı sonucu vermeye devam etmektedir. Dönen değeri sınayan
test böyle bir değişiklikten etkilenmez. Kayıt bu yüzden ancak çağrının kendisi bir
sözleşme parçasıysa sınanır: örneğin her paketin ayrı ayrı sorulması bir gereklilikse.

Üç bölümü bir arada okuduğumuzda ölçüm şunu söylüyor. Sahte nesne **kaynağın
değişkenliğini** ortadan kaldırır ve ayrı sonuç sayısını üçten bire indirir; buna karşılık
**kaynağın kendisiyle uyumu** hakkında hiçbir şey söylemez, çünkü gerçek kaynağa hiç
sormaz. Yalıtımın tekleştirdiği şey sonuçtur, doğruluk değildir — ve tekleşen sonucun
gerçekle bağını kuran tek düzenek, o bağı **ayrıca ölçen** bir sınamadır.

## Özet

- Dış kaynağa uzanan aynı test, kaynağın üç kipinde **3 ayrı sonuç** verir: tam yanıt,
  eksik yanıt ve hata. Üçü de kaynağın olağan işleyişindedir.
- Gecikmiş yansıma üç kipin en sessizidir: istek başarılıdır, yanıt geçerlidir ve yalnız
  bir paketin seçilen sürümü değişir.
- Sahte nesne yanıtı sabitleyerek sayıyı **1 ayrı sonuca** indirir ve testi kaynağın
  kiplerinden kurtarır; ayrıca aldığı çağrıları kaydederek dönen değerin ötesinde bir
  sınamaya izin verir.
- Depodan sapmış bir sahte de **1 ayrı sonuç** verir ve testi geçirir, çünkü beklenti
  sahteye bakılarak yazılmıştır; sahte ile gerçek kaynak arasında **2 ayrı sonuç** durur.
- Sözleşme sınaması aynı çağrıları iki kaynağa sorar ve sapmayı **1 pakette** bulur;
  yalıtımı bozduğu için birim testlerin yanında değil, ayrı bir küme olarak tutulur.

## Sonraki Adım

Buraya kadar sınanan her şey bir test dosyasında yazılıydı: iddia da, düzenek de, sahte
nesne de. Oysa bir projenin doğruluk iddiaları yalnız orada durmaz — belgelerde de durur.
Bir işlevin belge dizgisinde yazan "şu çağrı şunu döndürür" cümlesi bir iddiadır ve
hiçbir koşum onu sınamaz; kod değiştiğinde sessizce yanlışlanır. Sonraki ders bu iddiaları
sınanabilir hale getirir ve tek bir şeyi sayar: aynı belge, aynı değişiklikten sonra kaç
bayat iddiayı sese çevirir, kaçını sessiz bırakır.
