<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Umit Sinanoglu</title>
    <description>The latest articles on DEV Community by Umit Sinanoglu (@umit_sinanoglu_822402d535).</description>
    <link>https://dev.to/umit_sinanoglu_822402d535</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3176130%2F0db4b415-3d5c-4a73-8271-1e9a579b7564.jpg</url>
      <title>DEV Community: Umit Sinanoglu</title>
      <link>https://dev.to/umit_sinanoglu_822402d535</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/umit_sinanoglu_822402d535"/>
    <language>en</language>
    <item>
      <title># Mobil Test Otomasyonunda ThreadLocal ile Context Yönetimi: N Adet Cihazda Paralel Test Execution</title>
      <dc:creator>Umit Sinanoglu</dc:creator>
      <pubDate>Wed, 17 Dec 2025 07:13:58 +0000</pubDate>
      <link>https://dev.to/umit_sinanoglu_822402d535/-mobil-test-otomasyonunda-threadlocal-ile-context-yonetimi-n-adet-cihazda-paralel-test-execution-2n7i</link>
      <guid>https://dev.to/umit_sinanoglu_822402d535/-mobil-test-otomasyonunda-threadlocal-ile-context-yonetimi-n-adet-cihazda-paralel-test-execution-2n7i</guid>
      <description>&lt;h2&gt;
  
  
  📝 Giriş
&lt;/h2&gt;

&lt;p&gt;Modern mobil test otomasyonunda, testlerin hızlı ve güvenilir şekilde çalışması kritik önem taşır. Özellikle birden fazla cihazda aynı anda test koşmak (paralel execution), test süresini dramatik şekilde azaltır. Ancak bu yaklaşım, &lt;strong&gt;thread-safety&lt;/strong&gt; (thread güvenliği) konusunda ciddi zorluklar getirir.&lt;/p&gt;

&lt;p&gt;Bu yazıda, gerçek bir enterprise projede kullanılan &lt;strong&gt;ThreadLocal tabanlı TestContext yönetimi&lt;/strong&gt; ve &lt;strong&gt;paralel test execution&lt;/strong&gt; mimarisini detaylı olarak inceleyeceğiz. Bu mimari sayesinde:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;5-6 farklı cihazda&lt;/strong&gt; (Android/iOS) aynı anda test koşabilirsiniz&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Her thread'in izole context'i&lt;/strong&gt; olur - veri karışması (race condition) yaşanmaz&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Her cihaz için farklı:&lt;/strong&gt; Driver, Config, Device parametreleri, Test Data&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Thread-safety,&lt;/strong&gt; instability kaynaklarından birini ortadan kaldırır&lt;/li&gt;
&lt;li&gt;✅ Test süresi &lt;strong&gt;%70'e kadar&lt;/strong&gt; azalır&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎯 Problem: Paralel Testlerde Neden Thread Safety Gerekir?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Senaryo: 5 Android Cihazda Paralel Test
&lt;/h3&gt;

&lt;p&gt;Diyelim ki elimizde 5 Android cihaz var ve aynı test senaryolarını paralel çalıştırmak istiyoruz:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Thread-1 → Emulator-5554 (Port: 4723) → User: JOHN
Thread-2 → Emulator-5556 (Port: 4724) → User: ALFRED
Thread-3 → Emulator-5558 (Port: 4725) → User: BROWN
Thread-4 → Samsung S24 (Port: 4726) → User: CHARLIE
Thread-5 → Iphone 14 (Port: 4727) → User: STEVE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;❌ Eğer thread-safe olmayan bir yapı kullanırsanız:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ YANLIŞ: Static singleton AppiumDriver&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DriverManager&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;AppiumDriver&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// TÜM THREAD'LER BUNU PAYLAŞIR!&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;AppiumDriver&lt;/span&gt; &lt;span class="nf"&gt;getDriver&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Race condition!&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Ne olur?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Thread-1, Emulator-5554'e bağlanır&lt;/li&gt;
&lt;li&gt;Thread-2, aynı driver değişkenine Samsung S24'ü yazar&lt;/li&gt;
&lt;li&gt;Thread-1, artık yanlış cihaza (Samsung S24) komut gönderir! 💥&lt;/li&gt;
&lt;li&gt;Test data karışır, driver session'lar çakışır&lt;/li&gt;
&lt;li&gt;Sonuç: %30-40 başarısız test, güvenilmez sonuçlar&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;✅ Çözüm: ThreadLocal ile İzole Context Yönetimi&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;ThreadLocal Nedir?
ThreadLocal, Java'nın her thread için izole değişken sağlayan mekanizmasıdır.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Her thread için ayrı bir değer tutar&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ThreadLocal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AppiumDriver&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ThreadLocal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Thread-1 set eder → sadece Thread-1 görür&lt;/span&gt;
&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;androidDriver1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Thread-2 set eder → sadece Thread-2 görür  &lt;/span&gt;
&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;androidDriver2&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Thread-1 get eder → kendi driver'ını alır&lt;/span&gt;
&lt;span class="nc"&gt;AppiumDriver&lt;/span&gt; &lt;span class="n"&gt;myDriver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// androidDriver1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Analoji: Her thread'in kendi dolabı var. Thread-1 dolabına koyduğu eşyayı Thread-2 göremez.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🏗️ Mimari: 3 Katmanlı Context Yönetimi&lt;/strong&gt;&lt;br&gt;
Katman 1: TestContextManager (Singleton-like utility (stateless) + ThreadLocal)&lt;br&gt;
Görev: Her thread için izole TestContext instance'ı yönetir.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;core.context&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestContextManager&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ✅ ThreadLocal: Her thread için ayrı TestContext&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ThreadLocal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TestContext&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ThreadLocal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Thread'in context'ini al&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;TestContext&lt;/span&gt; &lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Thread'e context ata&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setContext&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TestContext&lt;/span&gt; &lt;span class="n"&gt;testContext&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testContext&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"TestContext cannot be null"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testContext&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Thread bitince temizle (Memory Leak önleme)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;removeContext&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Driver'ı kapat ve context'i temizle&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;cleanUp&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;TestContext&lt;/span&gt; &lt;span class="n"&gt;testContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testContext&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;testContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cleanUp&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Driver.quit()&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;✨ Özellikler:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Singleton-like utility (stateless) pattern: Sadece 1 TestContextManager instance'ı&lt;br&gt;
ThreadLocal storage: Her thread'in kendi TestContext kutusu&lt;br&gt;
Memory leak prevention: removeContext() ile ThreadLocal temizlenir&lt;/p&gt;

&lt;p&gt;Katman 2: TestContext (Her Thread'in Kendi Context'i)&lt;br&gt;
Görev: Driver, Config, Device bilgilerini ve Test Data'yı tutar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;core.context&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.appium.java_client.AppiumDriver&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.HashMap&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.Map&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestContext&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ✅ ThreadLocal: Her thread için ayrı driver&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ThreadLocal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AppiumDriver&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ThreadLocal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// ✅ Immutable: Thread'ler arası paylaşılan sabit veriler&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ConfigContext&lt;/span&gt; &lt;span class="n"&gt;configContext&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// ENV config (PREPROD/PROD)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;DeviceContext&lt;/span&gt; &lt;span class="n"&gt;deviceContext&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// Device parametreleri (UDID, port)&lt;/span&gt;

    &lt;span class="c1"&gt;// ✅ Mutable: Thread'in kendi test data'sı&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;testData&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// Senaryo içi değişkenler&lt;/span&gt;

    &lt;span class="c1"&gt;// ✅ Thread-specific durumlar&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;isLoggedIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;appLaunched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Constructor: Immutable context'ler alınır&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;TestContext&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ConfigContext&lt;/span&gt; &lt;span class="n"&gt;configContext&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;DeviceContext&lt;/span&gt; &lt;span class="n"&gt;deviceContext&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configContext&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;deviceContext&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ConfigContext and DeviceContext cannot be null"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;configContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;configContext&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deviceContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deviceContext&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;testData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Driver getter/setter (ThreadLocal)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;AppiumDriver&lt;/span&gt; &lt;span class="nf"&gt;getDriver&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setDriver&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AppiumDriver&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Cleanup&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;cleanUp&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;quit&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Config ve Device bilgilerine erişim&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ConfigContext&lt;/span&gt; &lt;span class="nf"&gt;getConfigContext&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;configContext&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;DeviceContext&lt;/span&gt; &lt;span class="nf"&gt;getDeviceContext&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;deviceContext&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;✨ Anahtar Noktalar:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ThreadLocal Driver: Her thread'in kendi AppiumDriver instance'ı&lt;br&gt;
Immutable Contexts: ConfigContext ve DeviceContext değiştirilemez (thread-safe)&lt;br&gt;
Mutable Test Data: testData Map, sadece o thread tarafından kullanılır&lt;br&gt;
State Management: isLoggedIn, appLaunched gibi thread-specific durumlar&lt;/p&gt;

&lt;p&gt;Katman 3: ConfigContext &amp;amp; DeviceContext (Immutable Data Holders)&lt;br&gt;
ConfigContext: Ortam Konfigürasyonu&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;core.context&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.Map&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConfigContext&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ✅ Final: Değiştirilemez&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;configData&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ConfigContext&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;configData&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;configData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;configData&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Config key cannot be null or empty."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;configData&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RuntimeException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Configuration key '"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"' not found in ConfigContext."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  DeviceContext: Cihaz Parametreleri
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package core.context;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class DeviceContext {
    // ✅ Unmodifiable Map: Dışarıdan değiştirilemez
    private final Map&amp;lt;String, String&amp;gt; deviceParameters;

    public DeviceContext(Map&amp;lt;String, String&amp;gt; deviceParameters) {
        if (deviceParameters == null || deviceParameters.isEmpty()) {
            throw new IllegalArgumentException("Device parameters cannot be null or empty");
        }
        // Deep copy + unmodifiable
        this.deviceParameters = Collections.unmodifiableMap(new HashMap&amp;lt;&amp;gt;(deviceParameters));
    }

    public String getParameter(String parameterName) {
        if (parameterName == null || parameterName.isEmpty()) {
            throw new IllegalArgumentException("Parameter name cannot be null or empty");
        }
        String value = this.deviceParameters.get(parameterName);
        if (value == null) {
            throw new IllegalArgumentException("No parameter found with name: " + parameterName);
        }
        return value;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;🎯 Neden Immutable?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Immutable (değiştirilemez) objeler:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Thread-safe'dir (race condition yok)&lt;/li&gt;
&lt;li&gt;Defensive programming sağlar&lt;/li&gt;
&lt;li&gt;Bug'ları azaltır (unexpected mutation yok)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🚀 Paralel Test Execution: TestNG Integration&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;TestNG Suite XML Yapılandırması&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Parallel_All_Devices_Suite.xml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- ✅ parallel="tests" : Her &amp;lt;test&amp;gt; tag'i ayrı thread'de çalışır --&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- ✅ thread-count="5" : Maksimum 5 thread aynı anda --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;suite&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"Parallel-All-Devices-Test-Suite"&lt;/span&gt; &lt;span class="na"&gt;parallel=&lt;/span&gt;&lt;span class="s"&gt;"tests"&lt;/span&gt; &lt;span class="na"&gt;thread-count=&lt;/span&gt;&lt;span class="s"&gt;"5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;listeners&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- ✅ Her &amp;lt;test&amp;gt; context’i başlarken TestListener çalışır --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;listener&lt;/span&gt; &lt;span class="na"&gt;class-name=&lt;/span&gt;&lt;span class="s"&gt;"core.listener.TestListener"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;listener&lt;/span&gt; &lt;span class="na"&gt;class-name=&lt;/span&gt;&lt;span class="s"&gt;"core.listener.ExecutionListener"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/listeners&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- THREAD 1: Emulator-5554 --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;test&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"Android-Emulator-5554"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;parameter&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"cucumber.filter.tags"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"@E5554"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;parameter&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"USER"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"JOHN"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;parameter&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"platformName"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Android"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;parameter&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"udid"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"emulator-5554"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;parameter&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"platformVersion"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"16"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;parameter&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"deviceName"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"emulator-5554"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

        &lt;span class="c"&gt;&amp;lt;!-- ✅ Farklı Appium port (aynı port'ta çakışma olmaz) --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;parameter&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"appium.port"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"4723"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;parameter&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"appium.server.url"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"http://localhost"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;classes&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;class&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"cucumber.runners.E5554_TestRunner"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/classes&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;/test&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- THREAD 2: Emulator-5556 --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;test&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"Android-Emulator-5556"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;parameter&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"cucumber.filter.tags"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"@E5556"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;parameter&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"USER"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"ALFRED"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;parameter&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"platformName"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Android"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;parameter&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"udid"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"emulator-5556"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;parameter&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"platformVersion"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"16"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;parameter&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"deviceName"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"emulator-5556"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;parameter&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"appium.port"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"4724"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;parameter&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"appium.server.url"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"http://localhost"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;classes&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;class&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"cucumber.runners.E5556_TestRunner"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/classes&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/test&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- THREAD 3, 4, 5 benzer şekilde... --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/suite&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;✨ Kritik Noktalar:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1 - parallel="tests": Her  tag'i farklı thread'de çalışır&lt;br&gt;
2 - thread-count="5": TestNG pool'da maksimum 5 thread olur&lt;br&gt;
3 - Farklı Appium port'ları: Her cihaz farklı Appium server'a bağlanır&lt;br&gt;
4 - UDID'ler farklı: Her thread farklı cihaza komut gönderir&lt;br&gt;
5 - Farklı USER: Her thread farklı test kullanıcısı kullanır&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;2. TestListener: Her Thread için Context Kurulumu&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;core.listener&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;core.context.*&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;core.driver.DriverManager&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.appium.java_client.AppiumDriver&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.testng.ITestContext&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.testng.ITestListener&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestListener&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ITestListener&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onStart&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ITestContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;testName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// ✅ TestNG XML'den parametreleri al&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCurrentXmlTest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getParameter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"USER"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCurrentXmlTest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getParameter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ENV"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;appiumPort&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCurrentXmlTest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getParameter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"appium.port"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;appiumServerUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCurrentXmlTest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getParameter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"appium.server.url"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;udid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCurrentXmlTest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getParameter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"udid"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;deviceName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCurrentXmlTest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getParameter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"deviceName"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&amp;gt;&amp;gt;&amp;gt; THREAD-{} STARTING: {} (Device: {}) &amp;lt;&amp;lt;&amp;lt;"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
                    &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentThread&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;threadId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;testName&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deviceName&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// ✅ 1. Device parametrelerini topla&lt;/span&gt;
        &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;deviceParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
        &lt;span class="n"&gt;deviceParams&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userName&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;deviceParams&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"env"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;deviceParams&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"udid"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;udid&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;deviceParams&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"deviceName"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deviceName&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;deviceParams&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"platformName"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCurrentXmlTest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getParameter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"platformName"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;deviceParams&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"platformVersion"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCurrentXmlTest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getParameter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"platformVersion"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

        &lt;span class="c1"&gt;// ✅ 2. AppiumDriver oluştur (bu thread için)&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;fullAppiumUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;appiumServerUrl&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;":"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;appiumPort&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="nc"&gt;DesiredCapabilities&lt;/span&gt; &lt;span class="n"&gt;caps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DesiredCapabilitiesUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createCapabilities&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deviceParams&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;AppiumDriver&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DriverManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createDriver&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fullAppiumUrl&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;caps&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// ✅ 3. Config ve Context oluştur&lt;/span&gt;
        &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;configData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ConfigHelper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getConfigMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userName&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;configData&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"appium.server.url"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fullAppiumUrl&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="nc"&gt;TestContext&lt;/span&gt; &lt;span class="n"&gt;testContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TestContext&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ConfigContext&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configData&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DeviceContext&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deviceParams&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// ✅ 4. Driver'ı context'e ata&lt;/span&gt;
        &lt;span class="n"&gt;testContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDriver&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// ✅ 5. Thread'e context'i ata (ThreadLocal)&lt;/span&gt;
        &lt;span class="nc"&gt;TestContextManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setContext&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testContext&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"✅ THREAD-{} READY: Driver initialized for {}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
                    &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentThread&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;threadId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;deviceName&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onFinish&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ITestContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;&amp;lt;&amp;lt; THREAD-{} FINISHING: {} &amp;gt;&amp;gt;&amp;gt;"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
                    &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentThread&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;threadId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

        &lt;span class="c1"&gt;// ✅ Driver'ı kapat ve context'i temizle&lt;/span&gt;
        &lt;span class="nc"&gt;TestContext&lt;/span&gt; &lt;span class="n"&gt;testContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TestContextManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getContext&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testContext&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;AppiumDriver&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;testContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDriver&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;quit&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// ✅ ThreadLocal'ı temizle (Memory leak önleme)&lt;/span&gt;
        &lt;span class="nc"&gt;TestContextManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;removeContext&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"✅ THREAD-{} CLEANED UP"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentThread&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;threadId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;🎯 Akış Detayı: Execution Lifecycle&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;graph TD
    A["🟢 TestNG Thread-1 Başlar"] --&amp;gt; B["📋 TestListener.onStart() çalışır"]
    B --&amp;gt; C["🔧 Thread-1 için Driver oluştur&amp;lt;br/&amp;gt;Emulator-5554, Port: 4723"]
    C --&amp;gt; D["📦 Thread-1 için TestContext oluştur"]
    D --&amp;gt; E["🔐 TestContextManager.setContext&amp;lt;br/&amp;gt;ThreadLocal'a kaydet"]
    E --&amp;gt; F["🎬 Cucumber senaryoları çalışır&amp;lt;br/&amp;gt;Thread-1'in context'i ile"]
    F --&amp;gt; G["🔚 TestListener.onFinish() çalışır"]
    G --&amp;gt; H["🧹 Driver.quit() → Context temizle&amp;lt;br/&amp;gt;ThreadLocal.remove()"]
    H --&amp;gt; I["⭕ Thread-1 biter"]

    style A fill:#90EE90
    style I fill:#FFB6C6
    style E fill:#87CEEB
    style F fill:#FFD700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;Aynı Anda :&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Thread-2&lt;/span&gt; → Emulator-5556 (Port: 4724)&lt;br&gt;
&lt;span&gt;Thread-3&lt;/span&gt; → Emulator-5558 (Port: 4725)&lt;br&gt;
&lt;span&gt;Thread-4&lt;/span&gt; → Samsung S24 (Port: 4727)&lt;br&gt;
&lt;span&gt;Thread-5&lt;/span&gt; → Iphone 15 (Port: 4728) &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔑 Anahtar Nokta:&lt;/strong&gt; Her thread kendi driver, kendi context, kendi test data'sı ile &lt;strong&gt;izole şekilde&lt;/strong&gt; çalışır!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔐 Race Condition'suz Test Data Paylaşımı&lt;/strong&gt;&lt;br&gt;
Problem: Senaryo İçinde Veri Paylaşımı&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gherkin"&gt;&lt;code&gt;&lt;span class="kn"&gt;Scenario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; Para transferi
  &lt;span class="nf"&gt;Given &lt;/span&gt;Kullanıcı giriş yapar
  &lt;span class="nf"&gt;When &lt;/span&gt;&lt;span class="s"&gt;"FROMACCOUNT"&lt;/span&gt; hesabından bakiye sorgulanır  &lt;span class="c"&gt;# 1000 TL&lt;/span&gt;
  &lt;span class="nf"&gt;And &lt;/span&gt;Bakiye context'e kaydedilir                  &lt;span class="c"&gt;# testData["balance"] = "1000"&lt;/span&gt;
  &lt;span class="nf"&gt;And &lt;/span&gt;500 TL transfer yapılır
  &lt;span class="nf"&gt;Then &lt;/span&gt;Bakiye &lt;span class="s"&gt;"500"&lt;/span&gt; TL olmalıdır                   &lt;span class="c"&gt;# testData["balance"] == "500" ?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;❌ Yanlış Yaklaşım: Static Map&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ TÜM THREAD'LER AYNI MAP'İ PAYLAŞIR!&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DataHolder&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;testData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;✅ Doğru Yaklaşım: TestContext İçinde Map&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// TestContext.java&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestContext&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ✅ Her thread'in kendi map'i&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;testData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Step Definition'da kullanım&lt;/span&gt;
&lt;span class="nd"&gt;@When&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{string} hesabından bakiye sorgulanır"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;queryBalance&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;accountKey&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;TestContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TestContextManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getContext&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;accountNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getConfigContext&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accountKey&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// API'den bakiye al&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApiHelper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBalance&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accountNumber&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// ✅ Bu thread'in context'ine kaydet&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;testData&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"balance"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Thread-{}: Balance = {}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentThread&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;threadId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Then&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bakiye {string} TL olmalıdır"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;verifyBalance&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;expectedBalance&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;TestContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TestContextManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getContext&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// ✅ Bu thread'in context'inden oku&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;actualBalance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;testData&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"balance"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nc"&gt;Assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actualBalance&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expectedBalance&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;🎯 Sonuç:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Thread-1 → testData["balance"] = "1000" (JOHN kullanıcısı)
Thread-2 → testData["balance"] = "500"  (ALFRED kullanıcısı)
Thread-3 → testData["balance"] = "2500" (BROWN kullanıcısı)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Her thread kendi verisini okur/yazar. &lt;strong&gt;Karışma olmaz!&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;📊 Pratik Örnek: 5 Cihazda Paralel Test&lt;/strong&gt;&lt;br&gt;
Maven Komutu&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mvn clean test -Dsurefire.suiteXmlFiles=src/test/resources/suites/Parallel_All_5_Devices_Suite.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Çalışma Zamanı Log Çıktısı&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;[INFO] Thread-15 &amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; STARTING TEST: Android-Emulator-5554 &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="gp"&gt;[INFO] Thread-16 &amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; STARTING TEST: Android-Emulator-5556 &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="gp"&gt;[INFO] Thread-17 &amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; STARTING TEST: Android-Emulator-5558 &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="gp"&gt;[INFO] Thread-18 &amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; STARTING TEST: Android-Emulator-5560 &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="gp"&gt;[INFO] Thread-19 &amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; STARTING TEST: Android-S24-Test &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="go"&gt;
[INFO] Thread-15: Creating driver for emulator-5554 on port 4723
[INFO] Thread-16: Creating driver for emulator-5556 on port 4724
[INFO] Thread-17: Creating driver for emulator-5558 on port 4725
[INFO] Thread-18: Creating driver for emulator-5560 on port 4726
[INFO] Thread-19: Creating driver for R5CX23A18QD on port 4727

[INFO] Thread-15: TestContext created for JOHN
[INFO] Thread-16: TestContext created for ALFRED
[INFO] Thread-17: TestContext created for BROWN
[INFO] Thread-18: TestContext created for CHARLIE
[INFO] Thread-19: TestContext created for STEVE

[INFO] Thread-15: Scenario 'Login Test' STARTED
[INFO] Thread-16: Scenario 'Transfer Test' STARTED
[INFO] Thread-17: Scenario 'Balance Query' STARTED
[INFO] Thread-18: Scenario 'Payment Test' STARTED
[INFO] Thread-19: Scenario 'Profile Update' STARTED

[INFO] Thread-15: testData["username"] = "JOHN"
[INFO] Thread-16: testData["amount"] = "500"
[INFO] Thread-17: testData["balance"] = "1000"

[INFO] Thread-15: Scenario 'Login Test' PASSED ✓
[INFO] Thread-16: Scenario 'Transfer Test' PASSED ✓
[INFO] Thread-17: Scenario 'Balance Query' PASSED ✓
[INFO] Thread-18: Scenario 'Payment Test' PASSED ✓
[INFO] Thread-19: Scenario 'Profile Update' PASSED ✓

[INFO] Thread-15: Driver quit successfully
[INFO] Thread-16: TestContext removed
[INFO] Thread-17: ThreadLocal cleaned up
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Dikkat&lt;/strong&gt;: Her thread kendi adını, kendi cihazını, kendi test data'sını loglayabiliyor!&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;🛠️ Appium Server Gereksinimleri&lt;/strong&gt;&lt;br&gt;
Paralel test için &lt;strong&gt;her cihaza ayrı Appium server&lt;/strong&gt; gerekir:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Terminal 1
appium --port 4723 --base-path /wd/hub

# Terminal 2
appium --port 4724 --base-path /wd/hub

# Terminal 3
appium --port 4725 --base-path /wd/hub

# Terminal 4
appium --port 4726 --base-path /wd/hub

# Terminal 5
appium --port 4727 --base-path /wd/hub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Alternatif: Docker ile Appium Grid&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Terminal 1
version: '3'
services:
  appium-1:
    image: appium/appium
    ports:
      - "4723:4723"

  appium-2:
    image: appium/appium
    ports:
      - "4724:4723"

  # ... 5 instance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;⚠️ Dikkat Edilmesi Gerekenler&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Memory Leak Önleme
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ✅ HER ZAMAN ThreadLocal'ı temizle
@Override
public void onFinish(ITestContext context) {
    TestContextManager.removeContext(); // ZORUNLU!
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Neden?&lt;/strong&gt; ThreadLocal temizlenmezse:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Thread pool'da thread yeniden kullanılır&lt;/li&gt;
&lt;li&gt;Eski context verisi kalır (memory leak)&lt;/li&gt;
&lt;li&gt;OutOfMemoryError riski artar&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Thread-Safe Collection Kullanımı
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Thread-safe collections&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConcurrentHashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;processed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Collections&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;synchronizedSet&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// ❌ Thread-safe OLMAYAN&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Race condition!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Static Değişkenlerden Kaçının
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Static mutable state (thread-safe değil)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestData&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userName&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// TÜM THREAD'LER PAYLAŞIR!&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ ThreadLocal veya TestContext içinde&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestContext&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userName&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Her thread'in kendi userName'i&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Sistem Kaynakları&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;5 paralel thread için minimum gereksinimler:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU: 8 core&lt;/li&gt;
&lt;li&gt;RAM: 16 GB (her thread için ~2-3 GB)&lt;/li&gt;
&lt;li&gt;Disk I/O: SSD önerilir (screenshot, log yazma hızı)&lt;/li&gt;
&lt;li&gt;Network: Her cihaz için stabil bağlantı&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;📈 Performans Karşılaştırması&lt;/strong&gt;&lt;br&gt;
Sequential (Tek Cihaz) vs Parallel (5 Cihaz)&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Metrik&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Sequential&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Parallel&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;İyileşme&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Test Sayısı&lt;/td&gt;
&lt;td&gt;100 senaryo&lt;/td&gt;
&lt;td&gt;100 senaryo&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Toplam Süre&lt;/td&gt;
&lt;td&gt;45 dakika&lt;/td&gt;
&lt;td&gt;12 dakika&lt;/td&gt;
&lt;td&gt;73% daha hızlı&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ortalama Senaryo Süresi&lt;/td&gt;
&lt;td&gt;27 saniye&lt;/td&gt;
&lt;td&gt;27 saniye&lt;/td&gt;
&lt;td&gt;Aynı&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU Kullanımı&lt;/td&gt;
&lt;td&gt;%25&lt;/td&gt;
&lt;td&gt;%85&lt;/td&gt;
&lt;td&gt;3.4x artış&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAM Kullanımı&lt;/td&gt;
&lt;td&gt;4 GB&lt;/td&gt;
&lt;td&gt;12 GB&lt;/td&gt;
&lt;td&gt;3x artış&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test Stability&lt;/td&gt;
&lt;td&gt;%92&lt;/td&gt;
&lt;td&gt;%90&lt;/td&gt;
&lt;td&gt;Minimal düşüş&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Sonuç: 5 cihazla paralel test, zamanı %73 azaltır ve stability hala %90 civarındadır!&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;🎓 Junior + Senior için Özet&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
Junior Seviye Özeti&lt;br&gt;
&lt;strong&gt;ThreadLocal nedir?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Her thread'in kendi dolabı olduğunu düşün&lt;/li&gt;
&lt;li&gt;Thread-1 dolabına koyduğu şeyi Thread-2 göremez&lt;/li&gt;
&lt;li&gt;ThreadLocal.set() ile koy, ThreadLocal.get() ile al&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Paralel test neden önemli?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;5 cihazda aynı anda test → 5 kat hızlı&lt;/li&gt;
&lt;li&gt;Her cihaz farklı Appium port'unda çalışır&lt;/li&gt;
&lt;li&gt;TestNG parallel="tests" ile otomatik thread yönetimi&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Kodda neye dikkat etmeli?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Static değişken kullanma (hepsi paylaşır)&lt;/li&gt;
&lt;li&gt;TestContext'i TestContextManager.getContext() ile al&lt;/li&gt;
&lt;li&gt;Test bittikten sonra removeContext() ile temizle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Senior Seviye Derinlemesine&lt;br&gt;
&lt;strong&gt;Mimari Kararlar:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1 - Singleton-like utility (stateless) + ThreadLocal Pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TestContextManager Singleton-like utility (stateless) (1 instance)&lt;/li&gt;
&lt;li&gt;ThreadLocal ile thread isolation&lt;/li&gt;
&lt;li&gt;Lazy initialization yerine eager initialization (TestListener'da)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2 - Immutable Context Design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ConfigContext ve DeviceContext immutable&lt;/li&gt;
&lt;li&gt;Defensive copy + Collections.unmodifiableMap&lt;/li&gt;
&lt;li&gt;Thread-safe by design (synchronized gerekmez)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3 - Memory Management:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ThreadLocal.remove() ile explicit cleanup&lt;/li&gt;
&lt;li&gt;Thread pool'da thread reuse → leak riski&lt;/li&gt;
&lt;li&gt;cleanUp() metodunda driver.quit() + context.remove()&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4 - Race Condition Prevention:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;testData Map Thread-confined olduğu için synchronization gerekmez&lt;/li&gt;
&lt;li&gt;ConcurrentHashMap kullanımı (shared state için)&lt;/li&gt;
&lt;li&gt;Synchronized bloklar yerine lock-free design&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;5 - TestNG Integration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ITestListener.onStart() → context setup&lt;/li&gt;
&lt;li&gt;ITestListener.onFinish() → context cleanup&lt;/li&gt;
&lt;li&gt;parallel="tests" + thread-count="N"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Karar&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Avantaj&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Dezavantaj&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ThreadLocal kullanımı&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Thread isolation, race condition yok&lt;/td&gt;
&lt;td&gt;Memory overhead, cleanup gerekli&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Immutable contexts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Thread-safe, defensive&lt;/td&gt;
&lt;td&gt;Flexibility düşük, yeniden oluşturma gerekir&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TestNG paralel execution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Kolay yapılandırma, ölçeklenebilir&lt;/td&gt;
&lt;td&gt;Thread pool sınırlı, resource yoğun&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Driver per thread&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;İzolasyon, stability&lt;/td&gt;
&lt;td&gt;Appium server sayısı artışı&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;🚀 Sonuç ve Best Practices&lt;/strong&gt;&lt;br&gt;
✅ Öneriler&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Her thread için izole context kullan (ThreadLocal)&lt;/li&gt;
&lt;li&gt;Immutable data structures tercih et (ConfigContext, DeviceContext)&lt;/li&gt;
&lt;li&gt;Thread-safe collections kullan (ConcurrentHashMap, synchronizedSet)&lt;/li&gt;
&lt;li&gt;ThreadLocal'ı MUTLAKA temizle (memory leak önleme)&lt;/li&gt;
&lt;li&gt;Her cihaz için ayrı Appium port (4723, 4724, 4725...)&lt;/li&gt;
&lt;li&gt;Test data'yı TestContext içinde tut (thread'ler arası izolasyon)&lt;/li&gt;
&lt;li&gt;Logging'de thread ID'yi ekle (debug kolaylığı)&lt;/li&gt;
&lt;li&gt;Sistem kaynaklarını hesaba kat (CPU, RAM, disk I/O)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;❌ Yapılmaması Gerekenler&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Static mutable state kullanma (race condition)&lt;/li&gt;
&lt;li&gt;Shared driver instance (thread'ler arasında paylaşım)&lt;/li&gt;
&lt;li&gt;ThreadLocal temizlemeyi unutma (memory leak)&lt;/li&gt;
&lt;li&gt;Aynı Appium port'unu kullanma (port conflict)&lt;/li&gt;
&lt;li&gt;Thread-safe olmayan collections (HashMap, ArrayList)&lt;/li&gt;
&lt;li&gt;Synchronized bloklara aşırı bağımlılık (performance bottleneck)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;📚 Daha Fazla Okuma&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://testng.org/doc/documentation-main.html#parallel-running" rel="noopener noreferrer"&gt;TestNG Parallel Execution Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/javase/8/docs/api/java/lang/ThreadLocal.html" rel="noopener noreferrer"&gt;Java ThreadLocal Javadoc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://appium.io/docs/en/about-appium/intro/" rel="noopener noreferrer"&gt;Appium Multi-Session Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.oreilly.com/library/view/effective-java/9780134686097/" rel="noopener noreferrer"&gt;Effective Java: Item 83 - Use lazy initialization judiciously&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;📝 Örnek Proje Yapısı&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ttb-qa-mobile-automation/
├── src/test/java/
│   ├── core/
│   │   ├── context/
│   │   │   ├── TestContextManager.java      # ⭐ Singleton (stateless) + ThreadLocal
│   │   │   ├── TestContext.java             # ⭐ Per-thread context
│   │   │   ├── ConfigContext.java           # ⭐ Immutable config
│   │   │   └── DeviceContext.java           # ⭐ Immutable device params
│   │   ├── driver/
│   │   │   └── DriverManager.java           # Driver oluşturma
│   │   └── listener/
│   │       └── TestListener.java            # ⭐ TestNG listener
│   └── cucumber/
│       ├── runners/
│       │   └── AndroidTestRunner.java       # Cucumber runner
│       ├── steps/
│       │   └── StepDefinitions.java         # Step definitions
│       └── features/
│           └── Login.feature                # Gherkin scenarios
└── src/test/resources/
    └── suites/
        └── Parallel_Android_5_Devices_Suite.xml  # ⭐ TestNG suite

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🎉 Sonuç&lt;br&gt;
Bu yazıda, &lt;strong&gt;ThreadLocal tabanlı context yönetimi&lt;/strong&gt; ve &lt;strong&gt;paralel test execution&lt;/strong&gt; mimarisini detaylı olarak inceledik. Bu yaklaşım sayesinde:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ 5-6 cihazda aynı anda test koşabilirsiniz&lt;/li&gt;
&lt;li&gt;✅ Thread-safe yapı ile race condition'dan kaçınırsınız&lt;/li&gt;
&lt;li&gt;✅ Test süresini %70'e kadar azaltırsınız&lt;/li&gt;
&lt;li&gt;✅ %90+ stability ile güvenilir sonuçlar alırsınız&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Ana mesaj:&lt;/strong&gt; Paralel test, doğru mimari ile hem hızlı hem de güvenilir olabilir. ThreadLocal ve immutable design patterns, bu mimarinin temel taşlarıdır.&lt;/p&gt;

&lt;p&gt;Sorularınız için: GitHub Issues veya yorumlar bölümünden ulaşabilirsiniz!&lt;/p&gt;

&lt;p&gt;Yazar: Ümit Sinanoğlu&lt;br&gt;
Tarih: 16 Aralık 2025&lt;br&gt;
Versiyon: 1.0&lt;br&gt;
Etiketler: #MobileAutomation #ThreadLocal #ParallelTesting #Appium #TestNG #Java&lt;/p&gt;

</description>
      <category>mobile</category>
      <category>testing</category>
      <category>automation</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
