Robot Framework ile Python Test Otomasyonu: Kapsamlı Bir Rehber

Üretim ortamında bir hata yakaladığınızda, o hatanın neden test aşamasında yakalanmadığını sorgularsınız. Çoğu zaman cevap basittir: testler ya yazılmamıştır ya da yazılmış ama koşturulmamıştır. Robot Framework tam bu noktada devreye giriyor ve test otomasyonunu sadece geliştiricilerin değil, QA mühendislerinin ve hatta kısmen sistem yöneticilerinin de anlayabileceği bir dile çeviriyor.

Ben birkaç yıldır hem Python tabanlı pytest/unittest hem de Robot Framework kullanan ekiplerde çalıştım. Her ikisinin de güçlü yanları var, ama Robot Framework’ün özellikle keyword-driven yaklaşımı, farklı teknik seviyelerdeki ekip üyelerini aynı test sürecine dahil etmek için eşsiz bir avantaj sunuyor. Bu yazıda Robot Framework’ü sıfırdan kurarak gerçek dünya senaryolarında nasıl kullanacağınızı, Python ile nasıl entegre edeceğinizi ve pytest ile birlikte nasıl çalıştırabileceğinizi ele alacağım.

Robot Framework Nedir ve Neden Kullanmalısınız

Robot Framework, Python tabanlı açık kaynaklı bir kabul testi ve otomasyon çerçevesidir. Ama onu özel kılan şey mimarisi değil, keyword-driven testing felsefesi. Testlerinizi neredeyse İngilizce cümle yazar gibi tanımlayabilirsiniz. Bu sayede “Bu test ne yapıyor?” sorusunun cevabını anlamak için koda bakmanıza gerek kalmaz.

pytest ile karşılaştırdığınızda şunu görürsünüz: pytest mükemmel bir birim test ve entegrasyon test aracıdır, Python bilgisi gerektirir ve geliştiricilere yöneliktir. Robot Framework ise sistem testleri, kabul testleri ve end-to-end senaryolar için daha uygun bir yapı sunar. Zaten bu ikisini birbirinin rakibi olarak görmemek gerekir. Birçok ekip her iki aracı aynı anda kullanır: pytest ile birim testleri, Robot Framework ile yüksek seviyeli kabul testleri.

Kurulum ve İlk Adımlar

Temel kurulum oldukça basit. Önce bir sanal ortam oluşturup gerekli paketleri yükleyelim:

python3 -m venv rf-env
source rf-env/bin/activate  # Windows: rf-envScriptsactivate

pip install robotframework
pip install robotframework-requests  # HTTP testleri için
pip install robotframework-seleniumlibrary  # Web UI testleri için
pip install pytest-robotframework  # pytest entegrasyonu için

# Kurulumu doğrula
robot --version

Proje yapınızı şu şekilde organize etmenizi öneririm:

mkdir -p rf-project/{tests,resources,libraries,results}
cd rf-project

# Dizin yapısı
# tests/         -> .robot test dosyaları
# resources/     -> ortak keyword ve değişken tanımları
# libraries/     -> özel Python kütüphaneleri
# results/       -> test çıktıları

İlk Robot Test Dosyası

Robot Framework testleri .robot uzantılı dosyalarda yazılır. Bu dosyalar birkaç bölümden oluşur: Settings , Variables , Test Cases ve Keywords . Basit bir örnekle başlayalım:

# tests/ilk_test.robot

*** Settings ***
Documentation    İlk Robot Framework test süiti
Library          OperatingSystem
Library          String

*** Variables ***
${PROJE_ADI}     SystemX
${VERSION}       2.0

*** Test Cases ***
Basit Matematiksel Doğrulama
    [Documentation]    Temel aritmetik işlemleri doğrular
    ${sonuc}=    Evaluate    2 + 2
    Should Be Equal As Numbers    ${sonuc}    4
    Log    Toplama işlemi başarılı: ${sonuc}

Değişken Birleştirme Testi
    [Documentation]    String işlemlerini doğrular
    ${tam_isim}=    Catenate    ${PROJE_ADI}    -    ${VERSION}
    Should Contain    ${tam_isim}    SystemX
    Length Should Be    ${tam_isim}    13

Dosya Sistemi Kontrolü
    [Documentation]    Geçici dizinin var olduğunu kontrol eder
    Directory Should Exist    /tmp
    ${dosyalar}=    List Files In Directory    /tmp
    Log    /tmp dizinindeki dosya sayısı: ${dosyalar}

Bu testi çalıştırmak için:

robot --outputdir results tests/ilk_test.robot

# Belirli bir test case çalıştırmak için
robot --test "Basit Matematiksel Doğrulama" tests/ilk_test.robot

# Tag ile filtreleme
robot --include smoke tests/

Özel Python Kütüphanesi Yazmak

Robot Framework’ün gerçek gücü özel kütüphaneler yazmaya başladığınızda ortaya çıkıyor. Python ile yazdığınız herhangi bir sınıf veya fonksiyon koleksiyonunu Robot keyword’üne dönüştürebilirsiniz:

# libraries/sunucu_kontrol.py

import subprocess
import socket
import os
from robot.api.deco import keyword, library

@library(scope='SUITE')
class SunucuKontrol:
    """Sistem yönetimi testleri için özel kütüphane."""
    
    ROBOT_LIBRARY_DOC_FORMAT = 'reST'
    
    def __init__(self, varsayilan_timeout=30):
        self.varsayilan_timeout = varsayilan_timeout
    
    @keyword('Port Açık Mı Kontrol Et')
    def port_acik_mi(self, host, port, timeout=None):
        """Verilen host ve portta bağlantı kurulabilir mi kontrol eder."""
        timeout = timeout or self.varsayilan_timeout
        try:
            with socket.create_connection((host, int(port)), timeout=float(timeout)):
                return True
        except (socket.timeout, ConnectionRefusedError, OSError) as e:
            raise AssertionError(f"{host}:{port} portu erişilebilir değil: {e}")
    
    @keyword('Servis Durumunu Al')
    def servis_durumu_al(self, servis_adi):
        """Systemd servisinin durumunu döner."""
        sonuc = subprocess.run(
            ['systemctl', 'is-active', servis_adi],
            capture_output=True,
            text=True
        )
        return sonuc.stdout.strip()
    
    @keyword('Servis Aktif Olmalı')
    def servis_aktif_olmali(self, servis_adi):
        """Servisin active durumda olduğunu doğrular."""
        durum = self.servis_durumu_al(servis_adi)
        if durum != 'active':
            raise AssertionError(
                f"'{servis_adi}' servisi aktif değil. Mevcut durum: {durum}"
            )
    
    @keyword('Disk Kullanımı Kontrol Et')
    def disk_kullanimi_kontrol_et(self, yol='/', maksimum_yuzde=80):
        """Disk kullanımının belirtilen eşiğin altında olduğunu doğrular."""
        import shutil
        toplam, kullanilan, bos = shutil.disk_usage(yol)
        yuzde = (kullanilan / toplam) * 100
        if yuzde > float(maksimum_yuzde):
            raise AssertionError(
                f"{yol} disk kullanımı %{yuzde:.1f} ile eşiği aşıyor (maks: %{maksimum_yuzde})"
            )
        return round(yuzde, 2)

Bu kütüphaneyi kullanan bir test dosyası:

# tests/sunucu_saglik_testleri.robot

*** Settings ***
Documentation    Sunucu sağlık kontrolleri
Library          ../libraries/sunucu_kontrol.py    varsayilan_timeout=10
Library          Collections

*** Variables ***
${YEREL_HOST}     localhost
${HTTP_PORT}      80
${SSH_PORT}       22

*** Test Cases ***
Kritik Portlar Erişilebilir Olmalı
    [Documentation]    Temel ağ servislerinin portlarını kontrol eder
    [Tags]             network    smoke
    Port Açık Mı Kontrol Et    ${YEREL_HOST}    ${SSH_PORT}
    Log    SSH portu erişilebilir

Disk Kullanımı Kritik Seviyede Değil
    [Documentation]    Ana bölümlerin disk doluluk oranını kontrol eder
    [Tags]             disk    smoke
    ${kullanim}=    Disk Kullanımı Kontrol Et    /    85
    Log    Kök bölüm disk kullanımı: %${kullanim}
    ${kullanim_var}=    Disk Kullanımı Kontrol Et    /var    90
    Log    /var disk kullanımı: %${kullanim_var}

SSH Servisi Aktif Olmalı
    [Documentation]    SSH daemon durumunu doğrular
    [Tags]             service    smoke
    Servis Aktif Olmalı    ssh

Resource Dosyaları ve Keyword Yeniden Kullanımı

Büyük test projelerinde ortak keyword’leri ve değişkenleri ayrı resource dosyalarında tutmak, bakımı ciddi ölçüde kolaylaştırır:

# resources/ortak_keywordlar.resource

*** Settings ***
Documentation    Tüm testlerde kullanılabilecek ortak keyword'ler
Library          RequestsLibrary
Library          Collections
Library          DateTime

*** Variables ***
${API_BASE_URL}       http://localhost:8080/api/v1
${ADMIN_USER}         admin
${ADMIN_PASS}         secret123
${ZAMAN_ASIMI}        30

*** Keywords ***
API Oturumu Başlat
    [Documentation]    API testleri için oturum oluşturur
    [Arguments]        ${base_url}=${API_BASE_URL}
    Create Session     api_oturumu    ${base_url}    timeout=${ZAMAN_ASIMI}
    [Return]           api_oturumu

HTTP GET İsteği At
    [Documentation]    GET isteği gönderir ve yanıtı döner
    [Arguments]        ${endpoint}    ${beklenen_durum}=200
    ${yanit}=          GET On Session    api_oturumu    ${endpoint}
    Status Code Should Be    ${yanit.status_code}    ${beklenen_durum}
    [Return]           ${yanit}

Zaman Damgası Al
    [Documentation]    İnsan okunabilir zaman damgası döner
    ${zaman}=          Get Current Date    result_format=%Y-%m-%d %H:%M:%S
    [Return]           ${zaman}

Suite Kurulumu Yap
    [Documentation]    Test suite başlangıcında yapılacak hazırlıklar
    Log    Test suite başladı: ${SUITE NAME}
    ${baslangic}=    Zaman Damgası Al
    Set Suite Variable    ${SUITE_BASLANGIC}    ${baslangic}

Suite Temizliği Yap
    [Documentation]    Test suite sonunda temizlik işlemleri
    ${bitis}=    Zaman Damgası Al
    Log    Test suite tamamlandı. Başlangıç: ${SUITE_BASLANGIC}, Bitiş: ${bitis}

API Test Senaryosu

Gerçek dünyada en çok kullandığım senaryolardan biri REST API testleri. RequestsLibrary ile bunu yapmak oldukça temiz:

# tests/api_testleri.robot

*** Settings ***
Documentation    REST API entegrasyon testleri
Resource         ../resources/ortak_keywordlar.resource
Library          RequestsLibrary
Library          JSONLibrary
Suite Setup      Suite Kurulumu Yap
Suite Teardown   Suite Temizliği Yap
Test Setup       API Oturumu Başlat

*** Variables ***
${KULLANICI_ENDPOINT}    /users
${SAGLIK_ENDPOINT}       /health

*** Test Cases ***
API Sağlık Kontrolü
    [Documentation]    Servisin ayakta olduğunu doğrular
    [Tags]             smoke    health
    ${yanit}=          HTTP GET İsteği At    ${SAGLIK_ENDPOINT}
    ${json}=           Set Variable    ${yanit.json()}
    Should Be Equal    ${json}[status]    ok
    Log    API sağlık durumu: ${json}[status]

Kullanıcı Listesi Alınabilmeli
    [Documentation]    Kullanıcı listeleme endpoint'ini doğrular
    [Tags]             regression    users
    ${yanit}=          HTTP GET İsteği At    ${KULLANICI_ENDPOINT}
    ${kullanici_listesi}=    Set Variable    ${yanit.json()}
    Should Not Be Empty    ${kullanici_listesi}
    Length Should Be Greater    ${kullanici_listesi}    0
    Log    Toplam kullanıcı sayısı: ${kullanici_listesi.__len__()}

Yeni Kullanıcı Oluşturulabilmeli
    [Documentation]    POST endpoint'i ile kullanıcı oluşturma
    [Tags]             regression    users    create
    ${yeni_kullanici}=    Create Dictionary
    ...    username=test_kullanici
    ...    [email protected]
    ...    role=viewer
    ${yanit}=          POST On Session    api_oturumu
    ...                ${KULLANICI_ENDPOINT}
    ...                json=${yeni_kullanici}
    Should Be Equal As Integers    ${yanit.status_code}    201
    ${olusturulan}=    Set Variable    ${yanit.json()}
    Should Be Equal    ${olusturulan}[username]    test_kullanici
    Set Suite Variable    ${OLUSTURULAN_KULLANICI_ID}    ${olusturulan}[id]

Oluşturulan Kullanıcı Silinebilmeli
    [Documentation]    Önceki testte oluşturulan kullanıcıyı temizler
    [Tags]             regression    users    delete
    [Setup]            Run Keyword If    '${OLUSTURULAN_KULLANICI_ID}' == '${EMPTY}'    Skip
    ${endpoint}=       Catenate    SEPARATOR=    ${KULLANICI_ENDPOINT}    /    ${OLUSTURULAN_KULLANICI_ID}
    ${yanit}=          DELETE On Session    api_oturumu    ${endpoint}
    Should Be Equal As Integers    ${yanit.status_code}    204

pytest ile Robot Framework Entegrasyonu

Bazı ekipler pytest altyapısını korurken Robot Framework testlerini de aynı pipeline’a dahil etmek ister. pytest-robotframework paketi tam burada devreye giriyor:

# pytest ile robot testlerini çalıştırmak
pip install pytest-robotframework

# pytest.ini veya pyproject.toml yapılandırması
cat > pytest.ini << 'EOF'
[pytest]
addopts = --robot-outputdir=results
testpaths = tests
EOF

# Robot testlerini pytest üzerinden çalıştır
pytest tests/sunucu_saglik_testleri.robot -v

# Hem Python hem Robot testlerini birlikte çalıştır
pytest tests/ -v --tb=short

Robot Framework sonuçlarını programatik olarak işlemek için robot.api modülünü kullanabilirsiniz:

# scripts/sonuc_analiz.py

from robot.api import ExecutionResult, ResultVisitor

class BasarisizTestToplayici(ResultVisitor):
    """Başarısız testleri toplayan visitor sınıfı."""
    
    def __init__(self):
        self.basarisiz_testler = []
        self.toplam = 0
        self.basarili = 0
    
    def visit_test(self, test):
        self.toplam += 1
        if test.status == 'FAIL':
            self.basarisiz_testler.append({
                'isim': test.name,
                'mesaj': test.message,
                'sure': test.elapsedtime
            })
        else:
            self.basarili += 1

def sonuclari_analiz_et(output_xml_yolu):
    """output.xml dosyasını analiz ederek özet çıkarır."""
    sonuc = ExecutionResult(output_xml_yolu)
    toplayici = BasarisizTestToplayici()
    sonuc.visit(toplayici)
    
    print(f"n{'='*50}")
    print(f"TEST SONUÇ ÖZETİ")
    print(f"{'='*50}")
    print(f"Toplam Test: {toplayici.toplam}")
    print(f"Başarılı:    {toplayici.basarili}")
    print(f"Başarısız:   {len(toplayici.basarisiz_testler)}")
    
    if toplayici.basarisiz_testler:
        print(f"nBaşarısız Testler:")
        for test in toplayici.basarisiz_testler:
            print(f"  - {test['isim']}")
            print(f"    Hata: {test['mesaj'][:100]}...")
    
    return len(toplayici.basarisiz_testler) == 0

if __name__ == '__main__':
    import sys
    xml_dosyasi = sys.argv[1] if len(sys.argv) > 1 else 'results/output.xml'
    basarili = sonuclari_analiz_et(xml_dosyasi)
    sys.exit(0 if basarili else 1)

CI/CD Pipeline Entegrasyonu

Testlerin CI/CD’ye entegrasyonu için minimal bir Makefile hazırlamak işleri hızlandırıyor:

# Makefile

.PHONY: test smoke regression clean report

ROBOT=robot
PYTEST=pytest
SONUC_DIR=results
TEST_DIR=tests

smoke:
	$(ROBOT) 
		--outputdir $(SONUC_DIR)/smoke 
		--include smoke 
		--loglevel INFO 
		--report smoke_raporu.html 
		$(TEST_DIR)/

regression:
	$(ROBOT) 
		--outputdir $(SONUC_DIR)/regression 
		--exclude skip 
		--loglevel DEBUG 
		--timestampoutputs 
		$(TEST_DIR)/

birim-testler:
	$(PYTEST) tests/unit/ -v --cov=libraries --cov-report=html

tam-test:
	$(MAKE) birim-testler
	$(MAKE) smoke
	$(MAKE) regression
	python scripts/sonuc_analiz.py $(SONUC_DIR)/regression/output.xml

temizlik:
	rm -rf $(SONUC_DIR)/
	find . -name "*.pyc" -delete
	find . -name "__pycache__" -type d -exec rm -rf {} +

rapor-sun:
	python3 -m http.server 8888 --directory $(SONUC_DIR)/regression &
	echo "Raporlar http://localhost:8888 adresinde"

Jenkins veya GitLab CI için tipik bir pipeline adımı:

# .gitlab-ci.yml içinden ilgili test stage

stages:
  - test

robot_testleri:
  stage: test
  image: python:3.11-slim
  before_script:
    - pip install robotframework robotframework-requests robotframework-seleniumlibrary
  script:
    - robot --outputdir results --xunit xunit_output.xml tests/
  after_script:
    - python scripts/sonuc_analiz.py results/output.xml || true
  artifacts:
    when: always
    paths:
      - results/
    reports:
      junit: results/xunit_output.xml
    expire_in: 7 days
  allow_failure: false

Sık Yapılan Hatalar ve Bunlardan Kaçınma

Yıllarca Robot Framework kullanan biri olarak şu hataları defalarca gördüm:

  • Her şeyi tek bir test dosyasına tıkmak. Bakımı imkânsız hale gelir. Suite bazlı mantıksal ayrım yapın.
  • Keyword isimlerini kısaltmak. “KK” yerine “Kullanıcı Kaydı Tamamla” yazın. Testler kendi kendini belgelemelidir.
  • Teardown yazmamak. Test başarısız olunca ortamı kirletir, sonraki testler etkilenir. Her kritik test için teardown şart.
  • Hardcoded değerler. IP adresi, kullanıcı adı, URL gibi değerleri doğrudan test içine yazmak yerine değişken veya dışarıdan inject edilen parametre kullanın.
  • Hata mesajlarını önemsememek. Should Be Equal gibi assertion’larda msg= parametresiyle açıklayıcı mesaj ekleyin. Gece 02:00’de incelenen hata raporu için bu küçük detay büyük fark yaratır.

Sonuç

Robot Framework, doğru kullanıldığında test otomasyonunu ekip genelinde paylaşılan bir sorumluluk haline getirebilir. Geliştiriciler Python kütüphaneleri yazar, QA mühendisleri bu kütüphaneleri kullanarak business language’da testler oluşturur, sistem yöneticileri de smoke testleri anlayıp yorumlayabilir. Bu hiyerarşi güzel çalışıyor.

pytest ile birlikte kullandığınızda ise elimde iki farklı silah oluyor: birim ve entegrasyon testleri için pytest’in esnekliği, sistem ve kabul testleri için Robot Framework’ün okunabilirliği. Bu iki aracı rakip değil tamamlayıcı olarak konumlandırmak, test stratejinizi çok daha sağlam temellere oturtacaktır.

Başlangıç için tavsiyem şu: Mevcut altyapınızın en kritik beş işlevini belirleyin ve bunlar için Robot Framework smoke testleri yazın. CI pipeline’ına bağlayın ve her deployment’ta otomatik koşturun. İlk hafta içinde bu testlerin değerini görürsünüz.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir