ADB + FFmpeg ile Android Cihaz Çiftliği: 10–50 Cihazda Otomatik UI Testi

@ridark_eth
İNGILIZCE1 gün önce · 07 Tem 2026
209K
104
10
22
175

TL;DR

Bu rehber; ADB ve FFmpeg kullanarak düzinelerce Android cihazda APK kurulumu, UI testi ve video raporlamayı otomatikleştiren bir Python hattını ve detaylı bir maliyet-fayda analizini içermektedir.

Uygulamanız düzinelerce gerçek telefonda, farklı üreticilerde, Android sürümlerinde ve ekran çözünürlüklerinde çalışmak zorunda olduğunda, manuel test hızla bir kabusa dönüşür. Aşağıda, bağlı tüm cihazları kendi kendine keşfeden, APK'yı hepsine paralel olarak yükleyen, enstrümante testleri çalıştıran, her çalıştırmanın videosunu kaydeden ve bu kayıtları FFmpeg ile tek bir video raporunda birleştiren bir Python hattı bulacaksınız.

Tüm yığın > Python + ADB + FFmpeg < standart QA araçlarıdır. Sihir yok, sadece angaryayı otomatikleştirmek.

Ridark - inline image

Hat mimarisi

text
1adb devices ──► seri numaraları listesi
2
3
4APK yükleme (tüm cihazlarda paralel olarak, ThreadPoolExecutor ile)
5
6
7her cihaz için:
8 screenrecord (arka planda) → am instrument (testleri çalıştır) → durdur + videoyu çek
9
10
11FFmpeg: her klibe seri numarasını ekle + birleştir ──► test_report.mp4

İhtiyacınız olacaklar

  • ADB (Android Debug Bridge) Android Platform Tools'tan -> cihaz kontrolü.
  • Python 3.10+ -> orkestrasyon (from __future__ olmadan list[str], tuple[...] kullanıyorum).
  • FFmpeg -> video işleme ve birleştirme.
  • USB hata ayıklaması etkinleştirilmiş, USB üzerinden (veya adb tcpip ile Wi-Fi üzerinden) bağlı cihazlar.

Tüm kod boyunca geçerli olan bir ilke: alt işlemlere argümanları bir liste olarak ve shell=True olmadan iletiyorum. Bu daha güvenlidir (dosya adları üzerinden enjeksiyon olmaz) ve yollardaki boşluklar veya özel karakterlerde sorun çıkarmaz.

1. Cihaz keşfi

Ridark - inline image

adb devices ayrıca yetkisiz / çevrimdışı durumdaki cihazları da listeler. Yalnızca gerçekten 'device' durumunda olanları tutuyoruz.

python
1import subprocess
2
3def get_devices() -> list[str]:
4 """'device' durumundaki tüm cihazların seri numaralarını döndürür."""
5 out = subprocess.run(
6 ["adb", "devices"],
7 capture_output=True, text=True, check=True,
8 ).stdout
9
10 serials: list[str] = []
11 for line in out.splitlines()[1:]: # ilk satır "List of devices" başlığıdır
12 line = line.strip()
13 if line.endswith("\tdevice"): # yetkisiz / çevrimdışı olanları atla
14 serials.append(line.split("\t")[0])
15 return serials

2. Paralel APK yükleme

50 cihaza tek tek kurulum yapmak yavaştır. İşi bir iş parçacığı havuzuna dağıtıyoruz: her adb install ayrı bir süreçtir, bu nedenle iş parçacıkları burada harika çalışır (CPU yakmak yerine G/Ç bekliyoruz).

python
1from concurrent.futures import ThreadPoolExecutor, as_completed
2
3def install_apk(serial: str, apk_path: str) -> tuple[str, bool, str]:
4 r = subprocess.run(
5 ["adb", "-s", serial, "install", "-r", "-g", apk_path],
6 capture_output=True, text=True,
7 )
8 ok = r.returncode == 0 and "Success" in r.stdout
9 return serial, ok, (r.stdout + r.stderr).strip()
10
11def install_on_all(apk_path: str, serials: list[str]) -> None:
12 with ThreadPoolExecutor(max_workers=len(serials) or 1) as pool:
13 futures = [pool.submit(install_apk, s, apk_path) for s in serials]
14 for f in as_completed(futures):
15 serial, ok, log = f.result()
16 print(f"[{'OK' if ok else 'FAIL'}] {serial}")
17 if not ok:
18 print(f" {log}")

Bayraklar: -r -> verileri koruyarak yeniden yükle, -g -> tüm çalışma zamanı izinlerini hemen ver (testlerin izin diyaloglarına takılmaması için kullanışlıdır).

3. Enstrümante testleri çalıştırma

am instrument, cihazda Espresso/JUnit testlerini çalıştırır. Başarı durumunda OK, başarısızlık durumunda FAILURES!!! yazdırır -> sonucu bu şekilde belirleriz.

python
1def run_instrumented_tests(
2 serial: str,
3 package: str,
4 runner: str = "androidx.test.runner.AndroidJUnitRunner",
5) -> tuple[str, bool]:
6 r = subprocess.run(
7 ["adb", "-s", serial, "shell", "am", "instrument", "-w",
8 f"{package}/{runner}"],
9 capture_output=True, text=True,
10 )
11 ok = "FAILURES!!!" not in r.stdout and r.returncode == 0
12 return serial, ok

package, test paketi kimliğidir, genellikle com.example.app.test.

4. Test sırasında ekranı kaydetme

screenrecord, videoyu doğrudan cihazda kaydeder. Akılda tutulması gereken sınırlamalar: dosya başına yaklaşık 3 dakikalık bir sınır ve ses yok. Kaydı arka planda başlatır, testi çalıştırır, ardından temiz bir şekilde durdurur ve dosyayı ana bilgisayara çekeriz.

Kaydı durdurmanın en güvenilir yolu, yerel adb'ye sinyal göndermek değil, cihazın kendisinde pkill kullanmaktır -> bu şekilde screenrecord MP4 kabını doğru şekilde sonlandırır.

python
1import time
2
3def start_recording(serial: str, remote: str = "/sdcard/run.mp4") -> subprocess.Popen:
4 return subprocess.Popen(
5 ["adb", "-s", serial, "shell", "screenrecord", remote]
6 )
7
8def stop_recording(
9 serial: str,
10 proc: subprocess.Popen,
11 remote: str = "/sdcard/run.mp4",
12 local: str = "run.mp4",
13) -> None:
14 # Cihazdaki SIGINT, screenrecord'un dosyayı düzgün şekilde kapatmasını sağlar
15 subprocess.run(["adb", "-s", serial, "shell", "pkill", "-SIGINT", "screenrecord"])
16 proc.wait(timeout=10)
17 time.sleep(1) # cihaza kabı sonlandırması için bir an ver
18 subprocess.run(["adb", "-s", serial, "pull", remote, local], check=True)

5. FFmpeg ile video raporunu birleştirme

Farklı cihazların farklı ekran çözünürlükleri vardır, bu nedenle bunları -c copy ile birleştiremezsiniz. Her klibi ortak bir formata (1080×1920) normalleştirir ve bu arada drawtext ile seri numarasını üst üste bindiririz. Bundan sonra tüm klipler aynıdır ve son birleştirme, yeniden kodlama gerektirmeyen hızlı bir concat işlemidir.

python
1def label_clip(src: str, dst: str, label: str) -> None:
2 """1080x1920'ye ölçekle ve bir etiket (cihaz seri numarası) ekle."""
3 vf = (
4 "scale=1080:1920:force_original_aspect_ratio=decrease,"
5 "pad=1080:1920:(ow-iw)/2:(oh-ih)/2,"
6 f"drawtext=text='{label}':x=20:y=20:fontsize=42:"
7 "fontcolor=white:box=1:[email protected]"
8 )
9 subprocess.run(
10 ["ffmpeg", "-y", "-i", src, "-vf", vf,
11 "-an", "-c:v", "libx264", "-preset", "veryfast", "-crf", "23", dst],
12 check=True,
13 )
14
15def concat_report(clips: list[str], out: str = "test_report.mp4") -> None:
16 with open("concat_list.txt", "w") as f:
17 for c in clips:
18 f.write(f"file '{c}'\n")
19 subprocess.run(
20 ["ffmpeg", "-y", "-f", "concat", "-safe", "0",
21 "-i", "concat_list.txt", "-c", "copy", out],
22 check=True,
23 )

6. Hepsini bir araya getirmek

python
1def main() -> None:
2 apk = "app-debug.apk"
3 package = "com.example.app.test"
4 runner = "androidx.test.runner.AndroidJUnitRunner"
5
6 serials = get_devices()
7 if not serials:
8 print("Cihaz bulunamadı. USB'yi ve 'adb devices' çıktısını kontrol edin.")
9 return
10
11 print(f"Bulunan cihaz sayısı: {len(serials)}")
12 install_on_all(apk, serials)
13
14 labeled: list[str] = []
15 for serial in serials:
16 proc = start_recording(serial)
17 _, ok = run_instrumented_tests(serial, package, runner)
18 stop_recording(serial, proc, local=f"{serial}.mp4")
19 print(f"[{'PASS' if ok else 'FAIL'}] {serial} üzerinde testler")
20
21 out = f"{serial}_labeled.mp4"
22 label_clip(f"{serial}.mp4", out, serial)
23 labeled.append(out)
24
25 concat_report(labeled, "test_report.mp4")
26 print("Tamamlandı: test_report.mp4")
27
28if __name__ == "__main__":
29 main()

Burada "kaydet + test" döngüsü cihazlar arasında sırayla çalışır -> bu şekilde daha okunabilirdir. Gerçek bir cihaz çiftliği için bu bloğu da bir ThreadPoolExecutor'a sarmak istersiniz, böylece tüm cihazlar aynı anda test edilir; mantık bölüm 2'deki kurulumla aynıdır.

Tekerleği yeniden icat etmeyin: hazır araçlar

Ridark - inline image
  • scrcpy -> bir cihazı PC'nizden gerçek zamanlı olarak yansıtma ve kontrol etme. Başarısız testlerde hata ayıklarken vazgeçilmezdir.
  • Appium / Espresso / UI Automator -> tam teşekküllü UI test çerçeveleri; yukarıdaki am instrument onların motorudur.
  • Gradle Managed Devices -> testleri doğrudan derlemeden emülatörlerde çalıştırın, manuel ADB uğraşması yok.
  • Firebase Test Lab / AWS Device Farm -> kendi donanımınızı bulundurmak istemiyorsanız bulut tabanlı gerçek cihaz filosu.
  • GNU parallel -> Python yerine bash'tan orkestrasyon yapmayı tercih ediyorsanız.

Ekonomi: maliyeti ve tasarrufu

Test otomasyonu "havadan para kazanmak"la ilgili değildir -> en pahalı iki kalemi kesmekle ilgilidir: insan-saati ve bulut dakikası. Aşağıda üç tipik ölçekte bir tahmin bulunmaktadır. Rakamlar gösterge niteliğindedir ve bölgeye, cihaz satıcısına ve sağlayıcı fiyatlandırmasına bağlıdır; satın almadan önce güncel oranları kontrol edin.

Kendi çiftliğiniz -> tek seferlik yatırım

Öğe

10 cihaz için

30 cihaz için

50 cihaz için

Kullanılmış Android telefonlar (~$60/adet)

~$600

~$1,800

~$3,000

Güçlü USB hub'lar

~$100

~$250

~$400

Mini-PC / ana bilgisayar

~$400

~$400

~$500

Kablolar, raf, diğer malzemeler

~$80

~$150

~$250

Tek seferlik toplam

~$1,200

~$2,600

~$4,150

Aylık elektrik

yok denecek kadar az

~$10–20

~$20–40

Bu bir sermaye harcamasıdır: bir kez ödeyin ve çiftlik yıllarca neredeyse hiç maliyetsiz çalışsın.

Bulut -> dakika başına ödeme yaparsınız

Firebase Test Lab, AWS Device Farm, BrowserStack ve benzerleri, cihaz-dakikası başına yaklaşık $0.05–0.20 ücret alır. 30 cihazda her biri 5 dakika süren tek bir regresyon çalıştırması, 150 cihaz-dakikasıdır, yani çalıştırma başına ~$7.5–30.

Şimdi CI sıklığıyla çarpın:

Çalıştırma sıklığı

Aylık çalıştırma sayısı

Maliyet (~$15/çalıştırma)

Günde 2× kez

~44

~$660/ay

Günde 10× kez

~220

~$3,300/ay

Her push'ta (aktif ekip)

500+

$7,500+/ay

Başabaş noktası: 30 cihazlı bir çiftlik (~$2,600), mütevazı bir günde 2 çalıştırma ile yaklaşık 4 ayda kendini amorti eder; aktif CI ile bir aydan kısa sürede. Ondan sonra bulut faturası her ay akmaya devam ederken, çiftlik akmaz.

Manuel iş gücü -> serbest kalan zaman

30 cihazda tek bir regresyon senaryosunun manuel olarak çalıştırılması, yaklaşık bir QA mühendisinin bir iş günü kadardır. Haftada iki kez regresyon çalıştırmak, ayda yaklaşık 8 kişi-gününe tekabül eder. Yaklaşık $1,600–2,700/ay olan bir QA maliyetiyle, bu, hattın "bağla–kur–dokun–kaydet" angaryasını ×30 yerine anlamlı işler için serbest bıraktığı maaşın önemli bir kısmıdır.

Bunun paraya nasıl dönüştüğü

Burada "komut dosyasından" doğrudan bir gelir yoktur -> ancak üç dolaylı ama çok gerçek mekanizma vardır:

  • Daha hızlı sürümler. Bir gün yerine dakikalar içinde regresyon → özellikleri daha sık yayınlarsınız → pazara daha hızlı yanıt verirsiniz. Bir abonelik ürünü için bu, doğrudan elde tutma ve gelire bağlanır.
  • Üretimde daha az hata. Belirli bir Samsung'ta bir çökmeyi sürümden önce yakalamak kuruşlara mal olur; aynı çökmenin kullanıcılara ulaşması, düşen mağaza puanlamaları, kayıp ve iadeler anlamına gelir. Erken yakalanan her hata, asla yazılmayan bir avuç olumsuz yorumdur.
  • Bir hizmet olarak satılır. Bir cihaz çiftliği kurmak ve CI testi yapmak, serbest çalışma ve dış kaynak kullanımında aranan bir roldür. Yukarıdaki hat, böyle bir teklif için hazır bir çekirdektir.

"Etkileşim çiftçiliği planlarından" temel fark: orada para, algoritmaları aldatmaktan gelir ve bir yasakla biter. Burada, kurtarılan saatlerden ve önlenen kayıplardan gelir. Birincisi çöker; ikincisi, bir müşteriye göstermekten utanmayacağınız sürdürülebilir bir iş senaryosudur.

Toparlama

Sonuç, tekrarlanabilir bir hattır: tek bir komut ve uygulama, tüm cihaz filosunda test edilir ve sonunda her bir cihazdaki davranışı gösteren bir video raporu elde edilir.

Para kazandıracak faydalı makaleleri kaçırmamak için yer imlerine ekleyin ve abone olun 📝

Tek tıkla kaydet

YouMind ile viral makaleleri AI derin okumayla incele

Save the source, ask focused questions, summarize the argument, and turn a viral article into reusable notes in one AI workspace.

Explore YouMind
Üreticiler için

Markdown'ınızı temiz bir 𝕏 makalesine dönüştürün

Kendi uzun yazılarınızı yayımlarken görselleri, tabloları ve kod bloklarını 𝕏 için biçimlendirmek zahmetlidir. YouMind, eksiksiz bir Markdown taslağını temiz ve hemen paylaşılabilir bir 𝕏 makalesine dönüştürür.

Markdown'dan 𝕏'e deneyin

Çözülecek daha fazla kalıp

Son viral makaleler

Daha fazla viral makale keşfet