{"id":41351,"date":"2026-04-12T17:00:46","date_gmt":"2026-04-12T14:00:46","guid":{"rendered":"https:\/\/fatihsoysal.com\/blog\/?p=41351"},"modified":"2026-04-12T17:00:46","modified_gmt":"2026-04-12T14:00:46","slug":"rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi","status":"publish","type":"post","link":"https:\/\/fatihsoysal.com\/blog\/rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi\/","title":{"rendered":"RxJS G\u00f6zlemlenebilirlerini Test Etmenin Sanat\u0131: Marble Test Y\u00f6ntemi"},"content":{"rendered":"<p><body><\/p>\n<h2>RxJS G\u00f6zlemlenebilirlerini Test Etmenin Sanat\u0131: Marble Test Y\u00f6ntemi<\/h2>\n<p>RxJS (Reactive Extensions for JavaScript) modern web uygulamalar\u0131nda asenkron veri ak\u0131\u015flar\u0131n\u0131 y\u00f6netmek i\u00e7in g\u00fc\u00e7l\u00fc bir paradigmad\u0131r. G\u00f6zlemlenebilirler (Observables) sayesinde, zamanla de\u011fi\u015fen veya olay tabanl\u0131 verileri bildirimli (declarative) bir \u015fekilde i\u015fleyebiliriz. Ancak, asenkron ve zaman ba\u011f\u0131ml\u0131 do\u011fas\u0131 gere\u011fi, RxJS g\u00f6zlemlenebilirlerini test etmek geleneksel birim test y\u00f6ntemleriyle zorlay\u0131c\u0131 olabilir. \u0130\u015fte bu noktada &#8220;Marble Testing&#8221; (Mermer Test Y\u00f6ntemi) devreye girer ve bize asenkron RxJS ak\u0131\u015flar\u0131n\u0131 deterministik ve okunabilir bir \u015fekilde test etme imkan\u0131 sunar.<\/p>\n<h3>RxJS ve G\u00f6zlemlenebilirlerin Test Edilmesindeki Zorluklar<\/h3>\n<p>RxJS, asenkron programlamay\u0131 basitle\u015ftirmeyi ama\u00e7layan bir k\u00fct\u00fcphanedir. Temel yap\u0131 ta\u015f\u0131 olan G\u00f6zlemlenebilirler, bir dizi de\u011feri zamanla yayan (emit eden) yap\u0131lar olarak d\u00fc\u015f\u00fcn\u00fclebilir. Bu de\u011ferler senkron veya asenkron olabilir. Bir g\u00f6zlemlenebilir, s\u0131f\u0131r veya daha fazla de\u011feri yayabilir, ard\u0131ndan bir tamamlama (completion) sinyali g\u00f6nderebilir veya bir hata (error) ile sonlanabilir.<\/p>\n<p>G\u00f6zlemlenebilirlerin bu do\u011fas\u0131, onlar\u0131 test etmeyi karma\u015f\u0131k hale getirir:<\/p>\n<p>1.  <strong>Asenkronluk:<\/strong> Geleneksel testler genellikle senkron kod \u00fczerine kuruludur. <code>setTimeout<\/code>, <code>setInterval<\/code> veya Promise tabanl\u0131 asenkron kodlar\u0131 test etmek i\u00e7in test \u00e7at\u0131lar\u0131n\u0131n (frameworks) asenkron yard\u0131mc\u0131lar\u0131n\u0131 (<code>done<\/code> callback, <code>async\/await<\/code>) kullan\u0131r\u0131z. Ancak RxJS ak\u0131\u015flar\u0131 birden fazla asenkron olay\u0131 ve zamanlamay\u0131 i\u00e7erebilir.<br \/>\n2.  <strong>Zaman Ba\u011f\u0131ml\u0131l\u0131\u011f\u0131:<\/strong> <code>delay<\/code>, <code>debounceTime<\/code>, <code>throttleTime<\/code>, <code>interval<\/code> gibi operat\u00f6rler, g\u00f6zlemlenebilirin yayma davran\u0131\u015f\u0131n\u0131 do\u011frudan zamanla ili\u015fkilendirir. Ger\u00e7ek zaman\u0131 bekleyen testler yava\u015f \u00e7al\u0131\u015f\u0131r ve &#8220;flaky&#8221; (karars\u0131z) olabilir. <code>jest.useFakeTimers()<\/code> gibi y\u00f6ntemler bir nebze yard\u0131mc\u0131 olsa da, RxJS&#8217;in karma\u015f\u0131k operat\u00f6r zincirlerini ve i\u00e7 i\u00e7e asenkronluklar\u0131 y\u00f6netmek hala zordur.<br \/>\n3.  <strong>Yan Etkiler ve Durum Y\u00f6netimi:<\/strong> G\u00f6zlemlenebilirler genellikle yan etkileri (side effects) tetikler veya d\u0131\u015f durumlar\u0131 etkiler. Bu yan etkilerin do\u011fru zamanda ve do\u011fru de\u011ferlerle ger\u00e7ekle\u015fti\u011finden emin olmak, testlerin kapsam\u0131n\u0131 geni\u015fletir.<br \/>\n4.  <strong>Abonelik ve Abonelikten \u00c7\u0131kma (Subscription\/Unsubscription) Y\u00f6netimi:<\/strong> \u00d6zellikle &#8220;hot&#8221; (s\u0131cak) g\u00f6zlemlenebilirler veya y\u00fcksek seviyeli operat\u00f6rler (<code>mergeMap<\/code>, <code>switchMap<\/code> vb.) kullan\u0131l\u0131rken, aboneliklerin ne zaman ba\u015flad\u0131\u011f\u0131n\u0131 ve bitti\u011fini izlemek \u00f6nemlidir. Bellek s\u0131z\u0131nt\u0131lar\u0131n\u0131 veya istenmeyen davran\u0131\u015flar\u0131 \u00f6nlemek i\u00e7in abonelik ya\u015fam d\u00f6ng\u00fcs\u00fcn\u00fcn do\u011fru \u00e7al\u0131\u015ft\u0131\u011f\u0131ndan emin olmak gerekir.<\/p>\n<p>Bu zorluklar kar\u015f\u0131s\u0131nda, RxJS ekibi ve toplulu\u011fu, bu t\u00fcr karma\u015f\u0131k senaryolar\u0131 deterministik ve \u00f6ng\u00f6r\u00fclebilir bir \u015fekilde test etmek i\u00e7in \u00f6zel bir yakla\u015f\u0131m geli\u015ftirmi\u015ftir: Marble Testing.<\/p>\n<h3>Marble Diyagramlar\u0131: RxJS Ak\u0131\u015flar\u0131n\u0131n G\u00f6rsel Dili<\/h3>\n<p>Marble diyagramlar\u0131, RxJS g\u00f6zlemlenebilirlerinin zaman i\u00e7indeki davran\u0131\u015f\u0131n\u0131 g\u00f6rsel olarak temsil eden bir notasyon sistemidir. Bu diyagramlar, hem test yazarken hem de RxJS operat\u00f6rlerinin nas\u0131l \u00e7al\u0131\u015ft\u0131\u011f\u0131n\u0131 anlamaya \u00e7al\u0131\u015f\u0131rken son derece faydal\u0131d\u0131r. Her bir diyagram bir zaman \u00e7izelgesini temsil eder ve \u00fczerindeki semboller belirli olaylar\u0131 g\u00f6sterir.<\/p>\n<p>Temel Marble Diyagram\u0131 Sembolleri:<\/p>\n<p>*   <strong><code>-<\/code> (Tire):<\/strong> Bir &#8220;frame&#8221; veya &#8220;time unit&#8221; (zaman birimi) temsil eder. Varsay\u0131lan olarak her bir tire 1 milisaniyeye kar\u015f\u0131l\u0131k gelir, ancak bu <code>TestScheduler<\/code> taraf\u0131ndan yap\u0131land\u0131r\u0131labilir. \u00d6rne\u011fin, <code>---<\/code> \u00fc\u00e7 zaman birimi ge\u00e7ti\u011fini g\u00f6sterir.<br \/>\n*   <strong><code>a<\/code>, <code>b<\/code>, <code>c<\/code>, &#8230; (Harfler):<\/strong> G\u00f6zlemlenebilir taraf\u0131ndan yay\u0131lan de\u011ferleri temsil eder. \u00d6rne\u011fin, <code>--a--b--<\/code> g\u00f6zlemlenebilirin iki zaman birimi sonra &#8216;a&#8217; de\u011ferini, ard\u0131ndan iki zaman birimi sonra &#8216;b&#8217; de\u011ferini yayd\u0131\u011f\u0131n\u0131 g\u00f6sterir.<br \/>\n*   <strong><code>|<\/code> (Dikey \u00c7izgi):<\/strong> G\u00f6zlemlenebilirin ba\u015far\u0131yla tamamland\u0131\u011f\u0131n\u0131 (complete) g\u00f6sterir. Bu, g\u00f6zlemlenebilirin art\u0131k ba\u015fka bir de\u011fer yaymayaca\u011f\u0131 anlam\u0131na gelir. \u00d6rne\u011fin, <code>--a--b|<\/code> &#8216;a&#8217; ve &#8216;b&#8217; de\u011ferlerini yayd\u0131ktan sonra tamamlan\u0131r.<br \/>\n*   <strong><code>#<\/code> (Kare\/Pound \u0130\u015fareti):<\/strong> G\u00f6zlemlenebilirin bir hata (error) ile sonland\u0131\u011f\u0131n\u0131 g\u00f6sterir. Hata durumunda g\u00f6zlemlenebilir tamamlanmaz. \u00d6rne\u011fin, <code>--a--#<\/code> &#8216;a&#8217; de\u011ferini yayd\u0131ktan sonra bir hata ile sonlan\u0131r.<br \/>\n*   <strong><code>()<\/code> (Parantezler):<\/strong> Ayn\u0131 zaman birimi i\u00e7inde senkron olarak yay\u0131lan birden fazla de\u011feri gruplamak i\u00e7in kullan\u0131l\u0131r. \u00d6rne\u011fin, <code>(ab)c--|<\/code> &#8216;a&#8217; ve &#8216;b&#8217; de\u011ferlerinin s\u0131f\u0131r zaman gecikmesiyle ayn\u0131 anda yay\u0131ld\u0131\u011f\u0131n\u0131, ard\u0131ndan &#8216;c&#8217; de\u011ferinin yay\u0131ld\u0131\u011f\u0131n\u0131 g\u00f6sterir.<br \/>\n*   <strong><code>^<\/code> (\u015eapka \u0130\u015fareti):<\/strong> &#8220;Hot&#8221; (s\u0131cak) g\u00f6zlemlenebilirlerde, aboneli\u011fin (subscription) ne zaman ba\u015flad\u0131\u011f\u0131n\u0131 g\u00f6sterir. Bu, genellikle <code>expectSubscriptions<\/code> ile kullan\u0131l\u0131r.<br \/>\n*   <strong><code>!<\/code> (\u00dcnlem \u0130\u015fareti):<\/strong> &#8220;Hot&#8221; g\u00f6zlemlenebilirlerde, aboneli\u011fin ne zaman sonland\u0131\u011f\u0131n\u0131 (unsubscribed) g\u00f6sterir.<\/p>\n<p>\u00d6rnek bir diyagram:<\/p>\n<pre><code class=\"language-\">Input:  --a--b--c|\nOutput: --A--B--C| (map(x => x.toUpperCase()))<\/code><\/pre>\n<p>Bu diyagram, giri\u015f g\u00f6zlemlenebilirinin &#8216;a&#8217;, &#8216;b&#8217;, &#8216;c&#8217; de\u011ferlerini belirli aral\u0131klarla yayd\u0131\u011f\u0131n\u0131 ve ard\u0131ndan tamamland\u0131\u011f\u0131n\u0131, \u00e7\u0131k\u0131\u015f g\u00f6zlemlenebilirinin ise bu de\u011ferleri b\u00fcy\u00fck harfe d\u00f6n\u00fc\u015ft\u00fcrerek ayn\u0131 zamanlamayla yayd\u0131\u011f\u0131n\u0131 g\u00f6sterir.<\/p>\n<h3>TestScheduler: Sanal Zaman\u0131n Y\u00f6neticisi<\/h3>\n<p>RxJS&#8217;in test paketi (<code>rxjs\/testing<\/code>), g\u00f6zlemlenebilirleri test etmek i\u00e7in \u00f6zel bir yard\u0131mc\u0131 s\u0131n\u0131f olan <code>TestScheduler<\/code>&#8216;\u0131 sa\u011flar. <code>TestScheduler<\/code>, asenkron RxJS kodunu senkron bir ortamda \u00e7al\u0131\u015ft\u0131rarak testlerin deterministik ve h\u0131zl\u0131 olmas\u0131n\u0131 sa\u011flar. Bunu, sanal bir zaman \u00e7izelgesi y\u00f6neterek yapar. <code>delay<\/code>, <code>debounceTime<\/code>, <code>interval<\/code> gibi zaman tabanl\u0131 operat\u00f6rler ger\u00e7ek zaman\u0131 beklemek yerine, <code>TestScheduler<\/code>&#8216;\u0131n sanal zaman\u0131n\u0131 kullan\u0131r.<\/p>\n<p><code>TestScheduler<\/code>&#8216;\u0131n temel \u00e7al\u0131\u015fma prensibi \u015f\u00f6yledir:<\/p>\n<p>1.  <strong>Sanal Zaman Kontrol\u00fc:<\/strong> T\u00fcm zaman tabanl\u0131 operat\u00f6rler, <code>TestScheduler<\/code> taraf\u0131ndan sa\u011flanan sanal zaman\u0131 kullanacak \u015fekilde otomatik olarak yama (patch)lan\u0131r. Bu sayede, <code>delay(100)<\/code> gibi bir operat\u00f6r ger\u00e7ekte 100ms beklemez, bunun yerine <code>TestScheduler<\/code>&#8216;\u0131n sanal zaman\u0131nda 100 birim ilerler.<br \/>\n2.  <strong><code>run()<\/code> Metodu:<\/strong> Test mant\u0131\u011f\u0131n\u0131z\u0131n \u00e7o\u011funu <code>scheduler.run()<\/code> metodunun i\u00e7inde yazars\u0131n\u0131z. Bu metot, kendisine verilen bir callback fonksiyonunu \u00e7al\u0131\u015ft\u0131r\u0131r ve bu fonksiyon i\u00e7indeki t\u00fcm RxJS operat\u00f6rlerinin <code>TestScheduler<\/code>&#8216;\u0131 kullanmas\u0131n\u0131 sa\u011flar. <code>run()<\/code> metodu tamamland\u0131\u011f\u0131nda, <code>TestScheduler<\/code> t\u00fcm sanal zaman i\u015flemlerini tamamlam\u0131\u015f olur.<br \/>\n3.  <strong><code>expectObservable()<\/code>:<\/strong> Bu metot, bir g\u00f6zlemlenebilirin beklenen de\u011ferleri ve zamanlamas\u0131yla e\u015fle\u015fip e\u015fle\u015fmedi\u011fini kontrol etmek i\u00e7in kullan\u0131l\u0131r. Giri\u015f olarak bir marble diyagram\u0131 al\u0131r.<br \/>\n4.  <strong><code>expectSubscriptions()<\/code>:<\/strong> \u00d6zellikle &#8220;hot&#8221; g\u00f6zlemlenebilirler veya y\u00fcksek seviyeli operat\u00f6rler ile \u00e7al\u0131\u015f\u0131rken, aboneliklerin ne zaman ba\u015flay\u0131p ne zaman bitti\u011fini kontrol etmek i\u00e7in kullan\u0131l\u0131r.<\/p>\n<h3>Marble Test Ortam\u0131n\u0131n Kurulumu<\/h3>\n<p>Marble testleri genellikle Jest, Mocha gibi test \u00e7at\u0131lar\u0131n\u0131n yan\u0131 s\u0131ra RxJS&#8217;in kendi <code>TestScheduler<\/code>&#8216;\u0131 ile birlikte kullan\u0131l\u0131r.<\/p>\n<p>1.  <strong>Ba\u011f\u0131ml\u0131l\u0131klar:<\/strong><br \/>\n    RxJS zaten projenizde y\u00fckl\u00fc olmal\u0131d\u0131r. <code>TestScheduler<\/code> direkt olarak <code>rxjs\/testing<\/code> paketinden gelir.<\/p>\n<pre><code class=\"language-bash\">npm install rxjs # Zaten y\u00fckl\u00fc de\u011filse<\/code><\/pre>\n<p>2.  <strong>Temel Test Yap\u0131s\u0131:<\/strong><\/p>\n<pre><code class=\"language-typescript\">import { TestScheduler } from 'rxjs\/testing';\n    import { of, map, delay } from 'rxjs';\n\n    describe('RxJS Operat\u00f6rleri Marble Testleri', () => {\n      let testScheduler: TestScheduler;\n\n      \/\/ Her testten \u00f6nce yeni bir TestScheduler \u00f6rne\u011fi olu\u015fturulur\n      beforeEach(() => {\n        testScheduler = new TestScheduler((actual, expected) => {\n          \/\/ Bu callback, expectObservable taraf\u0131ndan \u00e7a\u011fr\u0131l\u0131r\n          \/\/ ve beklenen ile ger\u00e7ek de\u011ferleri kar\u015f\u0131la\u015ft\u0131r\u0131r.\n          expect(actual).toEqual(expected);\n        });\n      });\n\n      it('should map values correctly', () => {\n        testScheduler.run(({ cold, expectObservable }) => {\n          \/\/ cold() metodu bir \"cold\" g\u00f6zlemlenebilir olu\u015fturur.\n          \/\/ Bu g\u00f6zlemlenebilir, her abone olundu\u011funda ba\u015ftan ba\u015flar.\n          const source = cold('-a--b--c|', { a: 1, b: 2, c: 3 });\n          const expected =    '-A--B--C|'; \/\/ Beklenen \u00e7\u0131kt\u0131 marble diyagram\u0131\n\n          const result = source.pipe(map(x => String.fromCharCode(x + 64))); \/\/ 1->A, 2->B, 3->C\n\n          \/\/ expectObservable, g\u00f6zlemlenebilirin beklenen marble diyagram\u0131yla e\u015fle\u015fti\u011fini do\u011frular.\n          expectObservable(result).toBe(expected, { A: 'A', B: 'B', C: 'C' });\n        });\n      });\n\n      it('should delay values', () => {\n        testScheduler.run(({ cold, expectObservable }) => {\n          const source = cold('a|', { a: 1 });\n          const expected =   '---a|'; \/\/ 2 birim gecikme\n\n          const result = source.pipe(delay(2, testScheduler)); \/\/ testScheduler'\u0131 operat\u00f6re ge\u00e7irmek \u00f6nemli\n\n          expectObservable(result).toBe(expected, { a: 1 });\n        });\n      });\n    });<\/code><\/pre>\n<p>    Yukar\u0131daki \u00f6rnekte <code>testScheduler.run()<\/code> metoduna bir obje destructuring ile <code>cold<\/code> ve <code>expectObservable<\/code> gibi yard\u0131mc\u0131 fonksiyonlar ge\u00e7ildi\u011fini g\u00f6r\u00fcyoruz. Bunlar <code>TestScheduler<\/code>&#8216;\u0131n <code>run<\/code> metodunun sa\u011flad\u0131\u011f\u0131 kolayla\u015ft\u0131r\u0131c\u0131lard\u0131r:<br \/>\n    *   <code>cold(marble, values, error)<\/code>: So\u011fuk (cold) g\u00f6zlemlenebilir olu\u015fturur.<br \/>\n    *   <code>hot(marble, values, error)<\/code>: S\u0131cak (hot) g\u00f6zlemlenebilir olu\u015fturur.<br \/>\n    *   <code>expectObservable(observable, subscriptionMarbles)<\/code>: Bir g\u00f6zlemlenebilirin davran\u0131\u015f\u0131n\u0131 do\u011frular.<br \/>\n    *   <code>expectSubscriptions(actualSubscriptionMarbles)<\/code>: Aboneliklerin ya\u015fam d\u00f6ng\u00fcs\u00fcn\u00fc do\u011frular.<br \/>\n    *   <code>flush()<\/code>: T\u00fcm bekleyen sanal zaman i\u015flemlerini hemen tamamlar.<br \/>\n    *   <code>time(marble)<\/code>: Bir marble string&#8217;indeki zaman birimini hesaplar.<\/p>\n<h3>Pratik Marble Test \u00d6rnekleri<\/h3>\n<p>\u015eimdi \u00e7e\u015fitli RxJS operat\u00f6rlerini ve senaryolar\u0131n\u0131 marble testlerle nas\u0131l test edebilece\u011fimize dair daha fazla \u00f6rnek inceleyelim.<\/p>\n<h4>Cold Observables (So\u011fuk G\u00f6zlemlenebilirler)<\/h4>\n<p>So\u011fuk g\u00f6zlemlenebilirler, her abone olundu\u011funda veri ak\u0131\u015f\u0131n\u0131 ba\u015ftan ba\u015flatan g\u00f6zlemlenebilirlerdir (\u00f6rn: <code>of<\/code>, <code>from<\/code>, <code>ajax<\/code>).<\/p>\n<pre><code class=\"language-typescript\">import { TestScheduler } from 'rxjs\/testing';\nimport { of, map, filter, delay, mergeMap } from 'rxjs';\n\ndescribe('Cold Observables Marble Tests', () => {\n  let testScheduler: TestScheduler;\n\n  beforeEach(() => {\n    testScheduler = new TestScheduler((actual, expected) => {\n      expect(actual).toEqual(expected);\n    });\n  });\n\n  it('should emit values from \"of\"', () => {\n    testScheduler.run(({ expectObservable }) => {\n      const source = of(1, 2, 3);\n      \/\/ of operat\u00f6r\u00fc senkron oldu\u011fu i\u00e7in de\u011ferler ayn\u0131 anda yay\u0131l\u0131r.\n      const expected = '(abc)|';\n      expectObservable(source).toBe(expected, { a: 1, b: 2, c: 3 });\n    });\n  });\n\n  it('should filter values', () => {\n    testScheduler.run(({ cold, expectObservable }) => {\n      const source = cold('-a-b-c|', { a: 1, b: 2, c: 3 });\n      const expected =   '---b--|'; \/\/ Sadece \u00e7ift say\u0131lar\u0131 filtrele\n\n      const result = source.pipe(filter(x => x % 2 === 0));\n\n      expectObservable(result).toBe(expected, { b: 2 });\n    });\n  });\n\n  it('should map and delay values', () => {\n    testScheduler.run(({ cold, expectObservable }) => {\n      const source = cold('-a--b|', { a: 1, b: 2 });\n      \/\/ Her de\u011fer 3 birim gecikmeli ve maplenmi\u015f olarak yay\u0131l\u0131r.\n      \/\/ 'a' (1) 1. birimde yay\u0131l\u0131r, 3 birim gecikme ile 4. birimde 'A' olarak \u00e7\u0131kar.\n      \/\/ 'b' (2) 4. birimde yay\u0131l\u0131r, 3 birim gecikme ile 7. birimde 'B' olarak \u00e7\u0131kar.\n      const expected =   '----A--B|';\n\n      const result = source.pipe(\n        map(x => String.fromCharCode(x + 64)),\n        delay(3, testScheduler)\n      );\n\n      expectObservable(result).toBe(expected, { A: 'A', B: 'B' });\n    });\n  });\n\n  it('should handle mergeMap with inner observable delays', () => {\n    testScheduler.run(({ cold, expectObservable }) => {\n      const source = cold('-a--b|', { a: 1, b: 2 });\n      \/\/ mergeMap i\u00e7indeki g\u00f6zlemlenebilirler e\u015f zamanl\u0131 \u00e7al\u0131\u015f\u0131r.\n      \/\/ a (1. birim) -> inner observable (3 birim gecikme) -> A (4. birim)\n      \/\/ b (4. birim) -> inner observable (3 birim gecikme) -> B (7. birim)\n      const expected =   '----A--B|';\n\n      const result = source.pipe(\n        mergeMap(x => of(String.fromCharCode(x + 64)).pipe(delay(3, testScheduler)))\n      );\n\n      expectObservable(result).toBe(expected, { A: 'A', B: 'B' });\n    });\n  });\n});<\/code><\/pre>\n<h4>Hot Observables (S\u0131cak G\u00f6zlemlenebilirler)<\/h4>\n<p>S\u0131cak g\u00f6zlemlenebilirler, verilerini abone olup olmamas\u0131na bak\u0131lmaks\u0131z\u0131n yaymaya ba\u015flayan g\u00f6zlemlenebilirlerdir (\u00f6rn: <code>Subject<\/code>, <code>shareReplay<\/code> ile olu\u015fturulan g\u00f6zlemlenebilirler, DOM olaylar\u0131). <code>hot()<\/code> ve <code>expectSubscriptions()<\/code> metodlar\u0131 s\u0131cak g\u00f6zlemlenebilirleri test etmek i\u00e7in kritik \u00f6neme sahiptir.<\/p>\n<pre><code class=\"language-typescript\">import { TestScheduler } from 'rxjs\/testing';\nimport { Subject, shareReplay } from 'rxjs';\n\ndescribe('Hot Observables Marble Tests', () => {\n  let testScheduler: TestScheduler;\n\n  beforeEach(() => {\n    testScheduler = new TestScheduler((actual, expected) => {\n      expect(actual).toEqual(expected);\n    });\n  });\n\n  it('should test a Subject', () => {\n    testScheduler.run(({ hot, expectObservable, expectSubscriptions }) => {\n      const subject = new Subject<string>();\n\n      \/\/ hot g\u00f6zlemlenebilir, subject'in ne zaman de\u011fer yayd\u0131\u011f\u0131n\u0131 sim\u00fcle eder.\n      \/\/ 10. birimde 'a', 20. birimde 'b' yayar.\n      const hotInput = '----------a---------b--|';\n\n      \/\/ Aboneli\u011fin ne zaman ba\u015flay\u0131p bitti\u011fini belirtiyoruz.\n      \/\/ 5. birimde abone ol, 25. birimde abonelikten \u00e7\u0131k.\n      const sub =      '-----^------------------!';\n      const expected = '----------a---------b--'; \/\/ Abonelik 25. birimde bitti\u011fi i\u00e7in complete sinyali al\u0131nmaz.\n\n      \/\/ hot g\u00f6zlemlenebilirin subject'e abone olmas\u0131n\u0131 sim\u00fcle et.\n      \/\/ Bu, subject'in de\u011ferleri yaymas\u0131n\u0131 sa\u011flar.\n      expectObservable(hot(hotInput)).subscribe(subject);\n\n      \/\/ Subject'e abone ol ve \u00e7\u0131kt\u0131y\u0131 do\u011frula.\n      expectObservable(subject, sub).toBe(expected, { a: 'a', b: 'b' });\n\n      \/\/ Subject'e olan aboneliklerin do\u011fru \u015fekilde y\u00f6netildi\u011fini kontrol et.\n      expectSubscriptions(subject.subscriptions).toBe(sub);\n    });\n  });\n\n  it('should test shareReplay operator', () => {\n    testScheduler.run(({ cold, hot, expectObservable, expectSubscriptions }) => {\n      const source = cold('-a--b--c|', { a: 1, b: 2, c: 3 });\n      const shared = source.pipe(shareReplay(1, undefined, testScheduler)); \/\/ testScheduler'\u0131 ge\u00e7meyi unutmay\u0131n\n\n      \/\/ Abonelik 5. birimde ba\u015flar, 15. birimde biter.\n      const sub1 = '-----^----------!';\n      \/\/ Abonelik 10. birimde ba\u015flar, 20. birimde biter.\n      const sub2 = '----------^---------!';\n\n      \/\/ \u0130lk abone (sub1) 5. birimde abone olur, 'a' de\u011ferini 6. birimde al\u0131r, 'b' de\u011ferini 9. birimde al\u0131r.\n      \/\/ 15. birimde abonelikten \u00e7\u0131kar. 'c' de\u011ferini g\u00f6rmez.\n      const expected1 = '------a--b----';\n\n      \/\/ \u0130kinci abone (sub2) 10. birimde abone olur. sharedReplay buffer'\u0131nda 'b' de\u011feri vard\u0131r.\n      \/\/ Bu y\u00fczden 'b' de\u011ferini hemen 10. birimde al\u0131r. Ard\u0131ndan 'c' de\u011ferini 12. birimde al\u0131r.\n      const expected2 = '----------b-c----';\n\n      \/\/ Source'a olan aboneli\u011fin ya\u015fam d\u00f6ng\u00fcs\u00fc.\n      \/\/ \u0130lk abone 5. birimde abone oldu\u011funda source'a abone olunur.\n      \/\/ \u0130kinci abone 10. birimde abone olsa da source zaten aktiftir.\n      \/\/ En son abone 20. birimde abonelikten \u00e7\u0131kt\u0131\u011f\u0131nda (veya buffer bo\u015fald\u0131\u011f\u0131nda) source'dan abonelikten \u00e7\u0131k\u0131l\u0131r.\n      \/\/ shareReplay(1) oldu\u011fu i\u00e7in, buffer'da bir de\u011fer oldu\u011fu s\u00fcrece source'a olan abonelik devam eder.\n      \/\/ Burada 20. birimde sub2 abonelikten \u00e7\u0131k\u0131nca, art\u0131k kimse dinlemedi\u011fi i\u00e7in source'dan da \u00e7\u0131k\u0131l\u0131r.\n      const sourceSubs = '-----^--------------!';\n\n      expectObservable(shared, sub1).toBe(expected1, { a: 1, b: 2 });\n      expectObservable(shared, sub2).toBe(expected2, { b: 2, c: 3 });\n\n      \/\/ shared g\u00f6zlemlenebilirin i\u00e7indeki source'a olan abonelikleri kontrol et.\n      expectSubscriptions(source.subscriptions).toBe(sourceSubs);\n    });\n  });\n});<\/code><\/pre>\n<h4>Hata ve Tamamlama Senaryolar\u0131<\/h4>\n<p>Hata (<code>#<\/code>) ve tamamlama (<code>|<\/code>) sembolleri, g\u00f6zlemlenebilirlerin ya\u015fam d\u00f6ng\u00fcs\u00fcn\u00fcn sonlanma durumlar\u0131n\u0131 test etmek i\u00e7in kullan\u0131l\u0131r.<\/p>\n<pre><code class=\"language-typescript\">import { TestScheduler } from 'rxjs\/testing';\nimport { of, throwError, catchError } from 'rxjs';\n\ndescribe('Error and Completion Marble Tests', () => {\n  let testScheduler: TestScheduler;\n\n  beforeEach(() => {\n    testScheduler = new TestScheduler((actual, expected) => {\n      expect(actual).toEqual(expected);\n    });\n  });\n\n  it('should complete successfully', () => {\n    testScheduler.run(({ cold, expectObservable }) => {\n      const source = cold('-a--b|');\n      const expected =   '-a--b|';\n      expectObservable(source).toBe(expected);\n    });\n  });\n\n  it('should emit an error', () => {\n    testScheduler.run(({ cold, expectObservable }) => {\n      const source = cold('-a--#');\n      const expected =   '-a--#';\n      expectObservable(source).toBe(expected);\n    });\n  });\n\n  it('should catch an error and resume', () => {\n    testScheduler.run(({ cold, expectObservable }) => {\n      const source = cold('-a--#', null, new Error('Test Error')); \/\/ Hata objesi de tan\u0131mlanabilir\n      \/\/ catchError, hata durumunda yeni bir g\u00f6zlemlenebilir d\u00f6nd\u00fcr\u00fcr.\n      \/\/ Burada hata yakaland\u0131ktan sonra 'b' de\u011feri yay\u0131l\u0131r ve tamamlan\u0131r.\n      const expected =   '-a--(b|)'; \/\/ 'b' de\u011feri hata yakaland\u0131ktan hemen sonra senkron yay\u0131l\u0131r.\n\n      const result = source.pipe(\n        catchError(err => of('b')) \/\/ Hata yakaland\u0131\u011f\u0131nda 'b' yay ve tamamla\n      );\n\n      expectObservable(result).toBe(expected, { a: undefined, b: 'b' }); \/\/ 'a' de\u011feri undefined \u00e7\u00fcnk\u00fc sadece marble string'de 'a' olarak ge\u00e7iyor\n    });\n  });\n});<\/code><\/pre>\n<h3>Marble Testlerin Faydalar\u0131<\/h3>\n<p>Marble testler, RxJS g\u00f6zlemlenebilirlerini test ederken bir\u00e7ok avantaj sunar:<\/p>\n<p>1.  <strong>Deterministik Testler:<\/strong> Sanal zaman kontrol\u00fc sayesinde, asenkron kodun davran\u0131\u015f\u0131n\u0131 her zaman ayn\u0131 \u015fekilde garanti eder. Ger\u00e7ek zaman gecikmeleri olmad\u0131\u011f\u0131 i\u00e7in testler &#8220;flaky&#8221; olmaz.<br \/>\n2.  <strong>Okunabilirlik ve Anla\u015f\u0131labilirlik:<\/strong> Marble diyagramlar\u0131, karma\u015f\u0131k zamanlamalar\u0131 ve olay ak\u0131\u015flar\u0131n\u0131 g\u00f6rsel bir dille ifade eder. Bu, test senaryolar\u0131n\u0131 okumay\u0131 ve anlamay\u0131 kolayla\u015ft\u0131r\u0131r. RxJS operat\u00f6rlerinin nas\u0131l \u00e7al\u0131\u015ft\u0131\u011f\u0131n\u0131 anlamak i\u00e7in de harika bir ara\u00e7t\u0131r.<br \/>\n3.  <strong>H\u0131zl\u0131 Test Y\u00fcr\u00fctme:<\/strong> Ger\u00e7ek zaman beklemeleri ortadan kalkt\u0131\u011f\u0131 i\u00e7in testler \u00e7ok daha h\u0131zl\u0131 \u00e7al\u0131\u015f\u0131r. Bu, b\u00fcy\u00fck projelerde geli\u015ftirme d\u00f6ng\u00fcs\u00fcn\u00fc h\u0131zland\u0131r\u0131r.<br \/>\n4.  <strong>Zaman Tabanl\u0131 Hatalar\u0131 Ay\u0131klama:<\/strong> G\u00f6zlemlenebilirlerin zamanlamas\u0131yla ilgili sorunlar\u0131 kolayca tespit etmeyi sa\u011flar. Hangi de\u011ferin ne zaman yay\u0131lmas\u0131 gerekti\u011fini a\u00e7\u0131k\u00e7a g\u00f6rebilirsiniz.<br \/>\n5.  <strong>Daha \u0130yi Tasar\u0131m:<\/strong> Marble testleri yazarken, g\u00f6zlemlenebilir ak\u0131\u015flar\u0131n\u0131z\u0131 daha mod\u00fcler ve test edilebilir bir \u015fekilde tasarlamaya te\u015fvik eder.<\/p>\n<h3>S\u0131n\u0131rlamalar ve Dikkat Edilmesi Gerekenler<\/h3>\n<p>Marble testler g\u00fc\u00e7l\u00fc olsa da, baz\u0131 s\u0131n\u0131rlamalar\u0131 vard\u0131r:<\/p>\n<p>*   <strong>\u00d6\u011frenme E\u011frisi:<\/strong> Marble diyagram\u0131 s\u00f6zdizimi ve <code>TestScheduler<\/code> API&#8217;si ba\u015flang\u0131\u00e7ta biraz \u00f6\u011frenme gerektirebilir.<br \/>\n*   <strong>Karma\u015f\u0131k Diyagramlar:<\/strong> \u00c7ok karma\u015f\u0131k veya uzun g\u00f6zlemlenebilir ak\u0131\u015flar\u0131 i\u00e7in marble diyagramlar\u0131 da okunmas\u0131 zor hale gelebilir. Bu t\u00fcr durumlarda, testleri daha k\u00fc\u00e7\u00fck, y\u00f6netilebilir birimlere b\u00f6lmek en iyisidir.<br \/>\n*   <strong>Ger\u00e7ek D\u00fcnya Asenkronlu\u011fu:<\/strong> Marble testler, ger\u00e7ek d\u00fcnya asenkronlu\u011funu (\u00f6rn: a\u011f istekleri, UI animasyonlar\u0131) sim\u00fcle etmek i\u00e7in tasarlanmam\u0131\u015ft\u0131r. Bu t\u00fcr senaryolar i\u00e7in genellikle mocklama (sahte nesneler kullanma) ve entegrasyon testleri daha uygundur. Marble testler daha \u00e7ok saf RxJS mant\u0131\u011f\u0131n\u0131 test etmek i\u00e7in idealdir.<\/p>\n<h3>En \u0130yi Uygulamalar<\/h3>\n<p>*   <strong>K\u00fc\u00e7\u00fck ve Odakl\u0131 Testler:<\/strong> Her bir testin belirli bir operat\u00f6r\u00fcn veya k\u00fc\u00e7\u00fck bir operat\u00f6r zincirinin davran\u0131\u015f\u0131n\u0131 do\u011frulamas\u0131na odaklan\u0131n.<br \/>\n*   <strong>A\u00e7\u0131klay\u0131c\u0131 De\u011ferler:<\/strong> Marble diyagramlar\u0131nda kullan\u0131lan de\u011ferler i\u00e7in a\u00e7\u0131klay\u0131c\u0131 harfler veya say\u0131lar kullan\u0131n ve <code>values<\/code> objesinde bu de\u011ferleri net bir \u015fekilde tan\u0131mlay\u0131n.<br \/>\n*   <strong><code>TestScheduler<\/code>&#8216;\u0131 Operat\u00f6rlere Ge\u00e7irme:<\/strong> <code>delay<\/code>, <code>debounceTime<\/code> gibi zaman tabanl\u0131 operat\u00f6rleri kullan\u0131rken, <code>TestScheduler<\/code> \u00f6rne\u011fini ikinci arg\u00fcman olarak ge\u00e7irmeyi unutmay\u0131n (<code>delay(100, testScheduler)<\/code>). Aksi takdirde, operat\u00f6r ger\u00e7ek zaman\u0131 kullanmaya \u00e7al\u0131\u015f\u0131r.<br \/>\n*   <strong><code>expectSubscriptions<\/code> Kullan\u0131m\u0131:<\/strong> \u00d6zellikle <code>hot<\/code> g\u00f6zlemlenebilirler ve y\u00fcksek seviyeli operat\u00f6rlerle \u00e7al\u0131\u015f\u0131rken, aboneliklerin do\u011fru \u015fekilde y\u00f6netildi\u011fini do\u011frulamak i\u00e7in <code>expectSubscriptions<\/code> kullanmaktan \u00e7ekinmeyin.<br \/>\n*   <strong>Di\u011fer Test Y\u00f6ntemleriyle Birle\u015ftirme:<\/strong> Marble testleri, RxJS ak\u0131\u015flar\u0131n\u0131n zamanlama ve veri d\u00f6n\u00fc\u015f\u00fcm mant\u0131\u011f\u0131n\u0131 test etmek i\u00e7in harikad\u0131r. Ancak, uygulaman\u0131z\u0131n di\u011fer k\u0131s\u0131mlar\u0131n\u0131 (\u00f6rn: UI etkile\u015fimleri, servis entegrasyonlar\u0131) test etmek i\u00e7in geleneksel birim testlerini, entegrasyon testlerini ve u\u00e7tan uca (end-to-end) testleri kullanmaya devam edin.<\/p>\n<h3>Sonu\u00e7<\/h3>\n<p>RxJS g\u00f6zlemlenebilirleri, modern reaktif uygulamalar geli\u015ftirmek i\u00e7in inan\u0131lmaz derecede g\u00fc\u00e7l\u00fc bir ara\u00e7t\u0131r. Ancak, asenkron ve zaman ba\u011f\u0131ml\u0131 do\u011falar\u0131 nedeniyle onlar\u0131 test etmek zorlu olabilir. Marble Testing, bu zorlu\u011fun \u00fcstesinden gelmek i\u00e7in tasarlanm\u0131\u015f, g\u00f6rsel, deterministik ve etkili bir yakla\u015f\u0131md\u0131r. <code>TestScheduler<\/code> ve marble diyagramlar\u0131 sayesinde, RxJS ak\u0131\u015flar\u0131n\u0131z\u0131n beklenen \u015fekilde \u00e7al\u0131\u015ft\u0131\u011f\u0131ndan emin olabilir, hatalar\u0131 daha kolay tespit edebilir ve kodunuzun kalitesini \u00f6nemli \u00f6l\u00e7\u00fcde art\u0131rabilirsiniz. RxJS projelerinizde Marble Testing&#8217;i benimsemek, daha sa\u011flam, bak\u0131m\u0131 kolay ve g\u00fcvenilir uygulamalar olu\u015fturman\u0131za yard\u0131mc\u0131 olacakt\u0131r.<\/body><\/p>\n","protected":false},"excerpt":{"rendered":"RxJS G\u00f6zlemlenebilirlerini Test Etmenin Sanat\u0131: Marble Test Y\u00f6ntemi RxJS (Reactive Extensions for JavaScript) modern web uygulamalar\u0131nda asenkron veri ak\u0131\u015flar\u0131n\u0131 y\u00f6netmek i\u00e7in g\u00fc\u00e7l\u00fc bir paradigmad\u0131r.","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"csco_page_header_type":"","csco_page_load_nextpost":"","csco_page_subscribe_form":"","csco_page_contact_form":"","footnotes":""},"categories":[1],"tags":[],"class_list":{"0":"post-41351","1":"post","2":"type-post","3":"status-publish","4":"format-standard","6":"category-genel","7":"cs-entry","8":"cs-video-wrap"},"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.5 (Yoast SEO v25.3.1) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>RxJS G\u00f6zlemlenebilirlerini Test Etmenin Sanat\u0131: Marble Test Y\u00f6ntemi - Kodlar\u0131n Gizemli D\u00fcnyas\u0131<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/fatihsoysal.com\/blog\/rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi\/\" \/>\n<meta property=\"og:locale\" content=\"tr_TR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"RxJS G\u00f6zlemlenebilirlerini Test Etmenin Sanat\u0131: Marble Test Y\u00f6ntemi\" \/>\n<meta property=\"og:description\" content=\"RxJS G\u00f6zlemlenebilirlerini Test Etmenin Sanat\u0131: Marble Test Y\u00f6ntemi RxJS (Reactive Extensions for JavaScript) modern web uygulamalar\u0131nda asenkron veri ak\u0131\u015flar\u0131n\u0131 y\u00f6netmek i\u00e7in g\u00fc\u00e7l\u00fc bir paradigmad\u0131r.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/fatihsoysal.com\/blog\/rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi\/\" \/>\n<meta property=\"og:site_name\" content=\"Kodlar\u0131n Gizemli D\u00fcnyas\u0131\" \/>\n<meta property=\"article:published_time\" content=\"2026-04-12T14:00:46+00:00\" \/>\n<meta name=\"author\" content=\"Fatih Soysal\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Yazan:\" \/>\n\t<meta name=\"twitter:data1\" content=\"Fatih Soysal\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tahmini okuma s\u00fcresi\" \/>\n\t<meta name=\"twitter:data2\" content=\"15 dakika\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi\/\"},\"author\":{\"name\":\"Fatih Soysal\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\"},\"headline\":\"RxJS G\u00f6zlemlenebilirlerini Test Etmenin Sanat\u0131: Marble Test Y\u00f6ntemi\",\"datePublished\":\"2026-04-12T14:00:46+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi\/\"},\"wordCount\":1840,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\"},\"inLanguage\":\"tr\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/fatihsoysal.com\/blog\/rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi\/#respond\"]}],\"copyrightYear\":\"2026\",\"copyrightHolder\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi\/\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi\/\",\"name\":\"RxJS G\u00f6zlemlenebilirlerini Test Etmenin Sanat\u0131: Marble Test Y\u00f6ntemi - Kodlar\u0131n Gizemli D\u00fcnyas\u0131\",\"isPartOf\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#website\"},\"datePublished\":\"2026-04-12T14:00:46+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi\/#breadcrumb\"},\"inLanguage\":\"tr\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/fatihsoysal.com\/blog\/rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Anasayfa\",\"item\":\"https:\/\/fatihsoysal.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"RxJS G\u00f6zlemlenebilirlerini Test Etmenin Sanat\u0131: Marble Test Y\u00f6ntemi\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#website\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/\",\"name\":\"Fatihsoysal.com\",\"description\":\"Blog - Yaz\u0131l\u0131m D\u00fcnyas\u0131 Tecr\u00fcbelerim\",\"publisher\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/fatihsoysal.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"tr\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\",\"name\":\"Fatih Soysal\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"tr\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png\",\"contentUrl\":\"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png\",\"width\":512,\"height\":512,\"caption\":\"Fatih Soysal\"},\"logo\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/\"},\"description\":\"Kullan\u0131m ve kodlama m\u00fckemmeliyetini odak alan uygulamalar olu\u015fturma deneyimine sahip, profesyonel olarak 15+ y\u0131l \u00fczeri deneyime sahip bir yaz\u0131l\u0131m m\u00fchendisi.\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/author\/fatihsoysal\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"RxJS G\u00f6zlemlenebilirlerini Test Etmenin Sanat\u0131: Marble Test Y\u00f6ntemi - Kodlar\u0131n Gizemli D\u00fcnyas\u0131","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/fatihsoysal.com\/blog\/rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi\/","og_locale":"tr_TR","og_type":"article","og_title":"RxJS G\u00f6zlemlenebilirlerini Test Etmenin Sanat\u0131: Marble Test Y\u00f6ntemi","og_description":"RxJS G\u00f6zlemlenebilirlerini Test Etmenin Sanat\u0131: Marble Test Y\u00f6ntemi RxJS (Reactive Extensions for JavaScript) modern web uygulamalar\u0131nda asenkron veri ak\u0131\u015flar\u0131n\u0131 y\u00f6netmek i\u00e7in g\u00fc\u00e7l\u00fc bir paradigmad\u0131r.","og_url":"https:\/\/fatihsoysal.com\/blog\/rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi\/","og_site_name":"Kodlar\u0131n Gizemli D\u00fcnyas\u0131","article_published_time":"2026-04-12T14:00:46+00:00","author":"Fatih Soysal","twitter_card":"summary_large_image","twitter_misc":{"Yazan:":"Fatih Soysal","Tahmini okuma s\u00fcresi":"15 dakika"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/fatihsoysal.com\/blog\/rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi\/#article","isPartOf":{"@id":"https:\/\/fatihsoysal.com\/blog\/rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi\/"},"author":{"name":"Fatih Soysal","@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1"},"headline":"RxJS G\u00f6zlemlenebilirlerini Test Etmenin Sanat\u0131: Marble Test Y\u00f6ntemi","datePublished":"2026-04-12T14:00:46+00:00","mainEntityOfPage":{"@id":"https:\/\/fatihsoysal.com\/blog\/rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi\/"},"wordCount":1840,"commentCount":0,"publisher":{"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1"},"inLanguage":"tr","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/fatihsoysal.com\/blog\/rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi\/#respond"]}],"copyrightYear":"2026","copyrightHolder":{"@id":"https:\/\/fatihsoysal.com\/blog\/#organization"}},{"@type":"WebPage","@id":"https:\/\/fatihsoysal.com\/blog\/rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi\/","url":"https:\/\/fatihsoysal.com\/blog\/rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi\/","name":"RxJS G\u00f6zlemlenebilirlerini Test Etmenin Sanat\u0131: Marble Test Y\u00f6ntemi - Kodlar\u0131n Gizemli D\u00fcnyas\u0131","isPartOf":{"@id":"https:\/\/fatihsoysal.com\/blog\/#website"},"datePublished":"2026-04-12T14:00:46+00:00","breadcrumb":{"@id":"https:\/\/fatihsoysal.com\/blog\/rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi\/#breadcrumb"},"inLanguage":"tr","potentialAction":[{"@type":"ReadAction","target":["https:\/\/fatihsoysal.com\/blog\/rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/fatihsoysal.com\/blog\/rxjs-gozlemlenebilirlerini-test-etmenin-sanati-marble-test-yontemi\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Anasayfa","item":"https:\/\/fatihsoysal.com\/blog\/"},{"@type":"ListItem","position":2,"name":"RxJS G\u00f6zlemlenebilirlerini Test Etmenin Sanat\u0131: Marble Test Y\u00f6ntemi"}]},{"@type":"WebSite","@id":"https:\/\/fatihsoysal.com\/blog\/#website","url":"https:\/\/fatihsoysal.com\/blog\/","name":"Fatihsoysal.com","description":"Blog - Yaz\u0131l\u0131m D\u00fcnyas\u0131 Tecr\u00fcbelerim","publisher":{"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/fatihsoysal.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"tr"},{"@type":["Person","Organization"],"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1","name":"Fatih Soysal","image":{"@type":"ImageObject","inLanguage":"tr","@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png","contentUrl":"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png","width":512,"height":512,"caption":"Fatih Soysal"},"logo":{"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/"},"description":"Kullan\u0131m ve kodlama m\u00fckemmeliyetini odak alan uygulamalar olu\u015fturma deneyimine sahip, profesyonel olarak 15+ y\u0131l \u00fczeri deneyime sahip bir yaz\u0131l\u0131m m\u00fchendisi.","url":"https:\/\/fatihsoysal.com\/blog\/author\/fatihsoysal\/"}]}},"yoast_meta":{"yoast_wpseo_title":"","yoast_wpseo_metadesc":"","yoast_wpseo_canonical":""},"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/posts\/41351","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/comments?post=41351"}],"version-history":[{"count":1,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/posts\/41351\/revisions"}],"predecessor-version":[{"id":41352,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/posts\/41351\/revisions\/41352"}],"wp:attachment":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/media?parent=41351"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/categories?post=41351"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/tags?post=41351"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}