---
title: 'Sahteleme ve Test Verisi'
source: 'https://academia.sh/tr/kurslar/ruby-uretim/sahteleme-ve-test-verisi'
course: 'Ruby Ekosistemi ve Üretim'
language: tr
updated: '2026-10-21T05:03:44+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Saati okuyan bir testin sonucunu ortamın belirlediğini ve saatin parametre olarak verilmesinin bunu koda çevirdiğini ölçer'
    - 'Kayıt tutan sahte nesne, taklit yanıt ve beklenti nesnesini birbirinden ayırır'
    - 'Sayaçlı bir test verisi üreticisinin her çağrıda farklı değer verdiğini ve yalıtımın üreticiyi testlere böldüğünü gösterir'
    - 'Sahteleme bir belirleyeni kaldırırken tohumlu test verisinin sıra belirleyenini bıraktığını ölçer'
---

# Sahteleme ve Test Verisi

Saati okuyan test neden yarın düşer? Ders saatin dışarıdan verilmesini, sahte nesneleri ve tohumlu test verisini ölçer; sahtelemenin hangi belirleyeni kaldırdığını ve hangisini bıraktığını ayırır.

Önceki ders bir test takımının sırayla düştüğünü gösterdi ve yalıtımla düşmeyi ortadan kaldırdı. Şimdi başka bir girdi var: bugünün tarihi. Bir test bugün geçiyor, otuz gün sonra düşüyor ve kodunda tek satır değişmedi. Bu dersin gözlemi `saati okuyan test` satırıdır: tabanda `true`, kanıt sütununda `B- S- O+`. Sonuç, saat değişince değişir; bağımlılık ve sıra değişince değişmez.

M13/K03'te bir değerin hangi isteğe kadar kaldığı soruldu: oturumda tutulan bir bilgi bir sonraki istekte hâlâ oradaydı. Burada aynı soru testlere uygulanır: bir test verisi hangi testten sonra hâlâ oradadır? Cevap, verinin kimin elinde olduğuna bağlıdır, ve bu ders onu ölçer. Çekirdeğin `belirleyen.rb` dosyası birebir yeniden verilir; blok kaynaktır ve `require_relative` ile çekilir.

```ruby
# belirleyen.rb — bir kosum modeli ve bir sonucu neyin belirledigini siniflandiran yardimci

# Kosum: sonucun bagli olabilecegi uc girdi. Bu uc girdinin disinda kalan her sey koddur.
Kosum = Data.define(:kilit, :tohum, :ortam)

TABAN = Kosum.new(
  kilit: { "bicimleyici" => "1.4.2", "ayristirici" => "2.0.1" },
  tohum: 1,
  ortam: { saat: Time.utc(2026, 10, 16, 9, 0), cekirdek: 4, degiskenler: {} }
)

# Her degisken tek bir girdiyi degistirir; otekiler tabandaki gibi kalir.
DEGISKENLER = {
  bagimlilik: TABAN.with(kilit: { "bicimleyici" => "1.5.0", "ayristirici" => "2.1.0" }),
  sira: TABAN.with(tohum: 2),
  ortam: TABAN.with(ortam: { saat: Time.utc(2027, 3, 1, 9, 0), cekirdek: 8,
                             degiskenler: { "ISCI_SAYISI" => "3" } })
}

# Kural: hesap once tabanda, sonra her degiskende bir kez kosar. Sonucu degistiren ilk
# degisken (bagimlilik, sira, ortam sirasiyla) belirleyendir; hicbiri degistirmiyorsa kod.
def belirleyen(hesap)
  taban = hesap.call(TABAN)
  degisti = DEGISKENLER.to_h { |ad, kosum| [ad, hesap.call(kosum) != taban] }
  tur = degisti.key(true)&.to_s || "kod"
  kanit = degisti.map { |ad, d| "#{ad[0].upcase}#{d ? '+' : '-'}" }.join(" ")
  [tur, kanit, taban]
end
```

## Saati Okuyan Test

Gözlemin hesabı küçük bir kuraldır: deneme süresi 30 gündür ve süre başlangıçtan beri geçen saniye sayısıyla ölçülür. Hesap saati parametre olarak alır; iki sınır değeri ölçelim.

```ruby
# saat.rb — deneme suresi 30 gundur
def deneme_suresinde?(baslangic, saat) = saat - baslangic < 30 * 86_400

baslangic = Time.utc(2026, 10, 1)
p deneme_suresinde?(baslangic, Time.utc(2026, 10, 30, 23, 59))
p deneme_suresinde?(baslangic, Time.utc(2026, 10, 31, 0, 0))
```
```
true
false
```

Sınır `30 * 86_400` saniyedir. 30 Ekim 23:59 süre içindedir; 31 Ekim 00:00 tam otuz gün geçmiştir ve `false` verir. Bu hesap saf bir hesaptır: aynı iki girdi her zaman aynı sonucu verir. Şu ana kadar hiçbir şey ortama bağlı değil. Bağlılık, saatin **nereden** geldiği sorulduğunda başlar.

Aşağıdaki dosya iki test yazar. Birincisi saati koşum modelinin ortamından okur, yani saati makinenin söylediği gibi alır. İkincisi aynı hesabı, başında verilmiş sabit bir saat kaynağıyla koşturur.

```ruby
# ortam.rb — saati kimin verdigi sonucu belirler
require_relative "belirleyen"

def deneme_suresinde?(baslangic, saat) = saat - baslangic < 30 * 86_400

baslangic = Time.utc(2026, 10, 1)
p deneme_suresinde?(baslangic, TABAN.ortam[:saat])
p deneme_suresinde?(baslangic, DEGISKENLER[:ortam].ortam[:saat])
p belirleyen(->(k) { deneme_suresinde?(baslangic, k.ortam[:saat]) })
```
```
true
false
["ortam", "B- S- O+", true]
```

Tabandaki saatle test geçti, Mart 2027'deki saatle düştü. Kural `ortam` dedi, kanıt `B- S- O+`: bağımlılık ve sıra değişkenlerinde sonuç aynı kaldı, ortam değişkeninde değişti. Kodun hiçbir satırı değişmeden aynı test iki sonuç verdi. Bu, saati okuyan bir testin yarın düşecek olan testtir: `Time.now` ile okunan saat, bugünkü koşumda tabandaki gibi, otuz gün sonra Mart gibi davranır. **RK19.** Bu ölçüm saati ortam modelinden okur; gerçek saatin değeri `Time.now` ile okunur ve sandbox'tan sandbox'a değiştiği için basılmaz.

## Saati Parametre Yapmak

Düzeltme, saati hesabın içinden çıkarıp dışarıdan vermektir. Hesabın saat kaynağını bir parametre olarak alması, saatin kim tarafından seçildiğini çağıran tarafa taşır. Üretimde kaynak gerçek saattir; testte kaynak sabit bir saattir. Bu sabit saat bir **sahte nesne**dir: gerçeğinin yerine geçen ve davranışı testin kontrol ettiği bir nesne.

```ruby
# sahte_saat.rb — saati disaridan veren test
require_relative "belirleyen"

def deneme_suresinde?(baslangic, saat) = saat - baslangic < 30 * 86_400
def kalan_deneme?(baslangic, saat_kaynagi) = deneme_suresinde?(baslangic, saat_kaynagi.call)

baslangic = Time.utc(2026, 10, 1)
sahte_saat = -> { Time.utc(2026, 10, 16, 9, 0) }

p belirleyen(->(k) { kalan_deneme?(baslangic, -> { k.ortam[:saat] }) })
p belirleyen(->(_k) { kalan_deneme?(baslangic, sahte_saat) })
```
```
["ortam", "B- S- O+", true]
["kod", "B- S- O-", true]
```

İlk satır saat kaynağı koşumun ortamından geldiğinde sonucu `ortam` olarak sınıflandırır. İkinci satır kaynağı sabit bir saatle değiştirir ve sonuç `kod` olur, kanıt `B- S- O-`. Hesap hâlâ aynı, test hâlâ aynı iddiada; değişen, saatin test tarafından seçilmesidir. Saat artık ortamın söylediği bir şey değil, testin kendi girdisidir. **RK20.** Sahte saat bir lambda ile kuruldu; gerçek bir çerçevenin zamanı dondurma yolu, örneğin `Time.now` yöntemini geçici olarak değiştirmesi, farklı bir kapsam taşır ve burada ölçülmedi.

Parametre yapmanın bir bedeli vardır ve ölçümün dışında kalmaz. Hesap artık bir saat kaynağı bekler; çağıran her yerin bunu geçmesi gerekir, ve üretimde geçilen kaynak gerçek saat olduğu için gerçek saatle çalışan yol testte hiç koşmaz. Sahte saatle yazılan test hesabı doğrular, saatin gerçek kaynaktan doğru okunduğunu doğrulamaz. Bu yüzden saati sahtelemek, saati okuyan testi yarın düşmekten korur ama okuma satırını denetimsiz bırakır. İyi bir ayrım, okumayı tek bir yerde toplamak ve o yeri ayrıca denemektir.

Sabit saatin ikinci bir yararı vardır: sınır değerleri denenebilir olur. Gerçek saatle 30. günün son dakikasını yakalamak için o dakikayı beklemek gerekir; sahte saatle iki çağrı yeter, ve ilk ölçümdeki `23:59` ile `00:00` ayrımı her koşumda aynı çıkar.

## Sahte Nesneler

Saat, sahtelenen en basit bağımlılıktır. Testlerde üç tür sahte nesne görülür ve her birinin işi farklıdır. **Kayıt tutan sahte nesne** gerçeğinin yaptığı işi yapmaz, yalnız kendisine ne söylendiğini kaydeder. **Taklit yanıt** verilen bir çağrıya hazır bir cevap döner. **Beklenti nesnesi** ise bir çağrının yapılıp yapılmadığını sonradan denetler.

```ruby
# bildirici.rb — kayit tutan sahte nesne
class SahteBildirici
  attr_reader :gonderilenler

  def initialize
    @gonderilenler = []
  end

  def gonder(alici, metin)
    @gonderilenler << [alici, metin]
  end
end

def fatura_bildir(bildirici, alici) = bildirici.gonder(alici, "fatura hazir")

bildirici = SahteBildirici.new
fatura_bildir(bildirici, "a@ornek.test")
p bildirici.gonderilenler
```
```
[["a@ornek.test", "fatura hazir"]]
```

Fatura bildirimi gerçek bir ileti göndermedi; sahte bildirici ne gönderildiğini bir diziye yazdı. Test iddiasını bu diziye kurabilir: `[["a@ornek.test", "fatura hazir"]]`. Kayıt tutan sahte nesne, bir çağrının **ne yaptığını** değil **kime ne söylediğini** ölçer. Üretimdeki bildirici ağa çıkardı; sahte bildirici ağa çıkmaz, bu yüzden test hem hızlıdır hem ağın durumundan bağımsızdır.

```ruby
# taklit.rb — taklit yanit
doviz = Object.new
doviz.define_singleton_method(:kur) { |_birim| 42.5 }

p doviz.kur(:usd)
p doviz.singleton_methods
```
```
42.5
[:kur]
```

Taklit yanıt bir hesap yapmadı: ne sorulursa sorulsun `42.5` döndü. Nesnenin yalnız bir çağrısı var ve o da testin yazdığı çağrı. Bu, bir testin dış bir kaynağa, örneğin bir kur servisine bağlılığını koparır; ama aynı zamanda sahte nesnenin gerçeği ne kadar temsil ettiği sorusunu doğurur. Sahte bildirici iki bağımsız değişken alıyordu; gerçek bildirici üçüncü bir değişken, kanal, isteyebilir.

```ruby
# ayrisma.rb — sahte nesne gercekten ayrisabilir
class SahteBildirici
  attr_reader :gonderilenler

  def initialize = @gonderilenler = []

  def gonder(alici, metin) = @gonderilenler << [alici, metin]
end

class GercekBildirici
  def gonder(alici, metin, kanal) = [alici, metin, kanal]
end

sahte = SahteBildirici.new
sahte.gonder("a@ornek.test", "fatura hazir")
p sahte.gonderilenler

begin
  GercekBildirici.new.gonder("a@ornek.test", "fatura hazir")
rescue ArgumentError => e
  p e.class
end
```
```
[["a@ornek.test", "fatura hazir"]]
ArgumentError
```

Sahte nesneyle yazılan çağrı geçti, aynı çağrı gerçek nesnede `ArgumentError` verdi. Test yeşildi, üretim kırıldı. **RK21.** Sahte nesne gerçeğin davranışını taklit eder, doğrulamaz; sahte ile gerçek arasındaki ayrışmayı yakalamak için ya gerçek nesneyle yapılan ayrı bir test ya da sahtenin gerçeğin imzasını izlemesini denetleyen bir iddia gerekir. Bu ölçüm yalnız ayrışmanın var olabileceğini gösterir; ayrışmayı otomatik yakalayan bir aracı ölçmez.

Beklenti nesnesi kayıt tutan nesneye bir karar ekler. Neyin çağrıldığını tutar, ama testin sonunda bunu bir iddiaya çevirir: "bu alıcıya tam bir kez bildirim gitmeliydi".

```ruby
# beklenti.rb — beklenti nesnesi
class BildirimBeklentisi
  def initialize(alici, beklenen_sayi)
    @alici = alici
    @beklenen_sayi = beklenen_sayi
    @cagrilar = []
  end

  def gonder(alici, _metin) = @cagrilar << alici

  def dogrula = @cagrilar.count(@alici) == @beklenen_sayi
end

beklenti = BildirimBeklentisi.new("a@ornek.test", 1)
beklenti.gonder("a@ornek.test", "fatura hazir")
p beklenti.dogrula
beklenti.gonder("a@ornek.test", "fatura hazir")
p beklenti.dogrula
```
```
true
false
```

Birinci çağrıdan sonra beklenti tutar, ikinci çağrıdan sonra tutmaz: aynı alıcıya iki bildirim gitmiştir. Ayrım şudur: kayıt tutan nesne bilgiyi toplar, beklenti nesnesi o bilgiye bir karar bağlar. Karar yanlış kurulursa test yanlış şeyi doğrular; beklenti sayısı testin kendi varsayımıdır.

## Test Verisi

Test verisi, bir testin girdisi olarak üretilen kayıtlardır: müşteri, fatura, kullanıcı. Veriyi elle yazmak her testte aynı satırları tekrarlar; bir **test verisi üreticisi** kayıtları bir kuralla üretir. En basit üretici bir sayaç tutar.

```ruby
# uretici.rb — sayacli test verisi ureticisi
def musteri_uretici
  sayac = 0
  -> { sayac += 1; "musteri#{sayac}" }
end

uret = musteri_uretici
p uret.call
p uret.call
p musteri_uretici.call
```
```
"musteri1"
"musteri2"
"musteri1"
```

Aynı üreticiden iki çağrı `"musteri1"` ve `"musteri2"` verdi: sayaç her çağrıda bir artar. Yeni bir üretici ise yeniden `"musteri1"` verdi. Bu iki gözlem yalıtımın cevabıdır. Bir üretici bütün testler arasında paylaşılırsa, bir testin ürettiği kayıt ötekinin sayacını ilerletir ve ikinci test `"musteri1"` yerine `"musteri2"` görür. Her testin kendi üreticisini kurması, paylaşılan durumu böler. Bu, önceki dersin ayar tablosundaki kopyalamanın aynısıdır, bir sayaç için. **RK22.** Ölçüm sayaçlı bir üreticiyi gösterir; veritabanında kalıcı bir kayıt üreten bir üreticinin aynı yalıtımı sağlaması için ayrıca temizlik adımı gerekir ve burada ölçülmedi.

## Sınırlayıcı Ölçüm

Sahtelemenin sonucu her şeyi çözmüş gibi görünür: saat sabit, bildirici sahte, sayaç yalıtılmış. Ama bir belirleyeni kaldırmak, ötekini kaldırmaz. Test verisi tohumla üretilirse sonucu tohum belirler.

```ruby
# veri.rb — tohumlu test verisi
require_relative "belirleyen"

p Array.new(3) { Random.new(1).rand(100) }.uniq.size
p (r = Random.new(1); Array.new(3) { r.rand(100) })
p (r = Random.new(2); Array.new(3) { r.rand(100) })

veri = lambda do |k|
  r = Random.new(k.tohum)
  Array.new(3) { r.rand(100) }
end
p belirleyen(veri)

sabit = lambda do |_k|
  r = Random.new(7)
  Array.new(3) { r.rand(100) }
end
p belirleyen(sabit)
```
```
1
[37, 12, 72]
[40, 15, 72]
["sira", "B- S+ O-", [37, 12, 72]]
["kod", "B- S- O-", [47, 68, 25]]
```

Birinci satır, her çağrıda yeni bir `Random.new(1)` kurulursa üç değerin hep aynı olduğunu gösterir: tek bir değer. Tohum 1 ve tohum 2 iki farklı üçlü verdi. `belirleyen` tohumu koşumdan aldığında sonuç `sira`: kanıt `B- S+ O-`, taban tohum 1'in üç değeri. Tohum sabit bir sayı olduğunda, yani `Random.new(7)`, sonuç `kod`: hesap koşumun tohumuna bakmıyor. **Sahteleme bir belirleyeni kaldırır, ötekini kendiliğinden kaldırmaz.** Saati sabitlemek ortamı kaldırdı; tohumu koşumdan alan veri üreticisi sıra belirleyenini bıraktı. Hangi belirleyenin kaldığı, bir şeyin sahte olmasından değil, sonucun neye bakmaya devam ettiğinden anlaşılır. **RK23.** Tohumlu veri burada üç sayıdan ibarettir; gerçek bir veri üreticisinin kayıtları tohuma bağlı olduğunda testin neden bazen düştüğü, bir önceki dersin aynı nedenidir ve ayrıca ölçülmedi.

Bu ayrımın pratik sonucu şudur: bir testi kararlı yapmak için hangi belirleyenin sonucu oynattığı önce ölçülmelidir. Saati sahtelemek, sonucu sıraya bağlı bir testi kurtarmaz; sıralamayı sabitlemek, saate bağlı bir testi kurtarmaz. Önceki dersin tohum tablosu ile bu dersin saat ölçümü aynı soruyu iki ayrı değişkende sorar, ve cevap her seferinde değişkenin kendisine bağlıdır.

## Özet

- Saati okuyan bir test ortamın söylediğine bağlıdır: `saati okuyan test` satırında kanıt `B- S- O+`, Mart 2027'de sonuç değişir.
- Bu dersin belirleyeni **ortam**dır. Saat parametre olarak verilince ve sabit bir saat kaynağı kullanılınca sonuç `kod` olur.
- Kayıt tutan sahte nesne ne söylendiğini toplar, taklit yanıt hazır bir cevap döner, beklenti nesnesi çağrıya bir karar bağlar.
- Sayaçlı test verisi üreticisi her çağrıda farklı değer verir; her testin kendi üreticisi paylaşılan durumu böler.
- Sahteleme bir belirleyeni kaldırır, ötekini kendiliğinden kaldırmaz: koşumun tohumundan üretilen test verisi `sira` olarak kalır.

## Sonraki Adım

Bu kursun ilk yarısı paketlerin ve testlerin sonucu neyin belirlediğini ölçtü: dosya adını paket tanımı, kurulan sürümü kilit dosyası, düşen testi sıra, saati okuyan testi ortam. İkinci yarı üretimde çalışan koda geçer. Eşzamanlılık dersi, aynı kodun kilitsiz bir sayaçla koşarken tohuma göre farklı sonuç verdiğini ölçer ve dışlama kilidinin bu farkı neden kaldırdığını gösterir.
