Testing Strategy #
Testing Ansible berbeda dari testing aplikasi. Kamu tidak sedang menguji fungsi yang deterministik — kamu menguji efek samping pada sistem nyata. Apakah nginx benar-benar terinstal? Apakah konfigurasi yang dihasilkan valid? Apakah service berjalan setelah role dieksekusi? Ini membutuhkan pendekatan yang berbeda: lingkungan yang bisa dibuat dan dihancurkan, assertions terhadap state sistem, dan strategi untuk menjaga test suite tetap berguna tanpa menjadi beban yang lebih berat dari manfaatnya.
Piramida Testing untuk Ansible #
╔════════════════╗
║ Integration ║ ← Sedikit, mahal, paling mendekati production
╠════════════════╣
║ Molecule ║ ← Per role, test di container
╠════════════════╣
║ Syntax Check ║ ← Per playbook/role
╠════════════════╣
║ ansible-lint ║ ← Per commit, paling cepat
╚════════════════╝
Banyak, murah
Investasi terbesar ada di base — linting dan syntax check yang cepat memberikan umpan balik paling cepat dengan biaya paling rendah.
Layer 1: Lint dan Syntax Check (Detik) #
# Jalankan di pre-commit hook dan di setiap PR
ansible-lint --profile production
# Syntax check semua playbook
for playbook in playbooks/*.yml; do
ansible-playbook "$playbook" --syntax-check -i inventory/staging/
done
# Check mode: simulasikan perubahan tanpa menerapkannya
ansible-playbook -i inventory/staging/ site.yml --check --diff
Ini harus berjalan dalam hitungan detik dan menjadi gate pertama sebelum kode bisa masuk ke repository.
Layer 2: Molecule Test (Menit) #
Setiap role harus punya Molecule scenario. Fokus test pada efek yang dapat diverifikasi:
# roles/postgresql/molecule/default/verify.yml
---
- name: Verify PostgreSQL installation
hosts: all
gather_facts: false
tasks:
- name: Verifikasi postgresql terinstal
command: psql --version
register: psql_version
changed_when: false
failed_when: psql_version.rc != 0
- name: Verifikasi service aktif dan enabled
systemd:
name: postgresql
register: pg_service
failed_when:
- pg_service.status.ActiveState != 'active'
- pg_service.status.UnitFileState != 'enabled'
- name: Verifikasi port 5432 terbuka
wait_for:
port: 5432
timeout: 10
state: started
- name: Verifikasi koneksi database berhasil
command: >
psql -U postgres -c "SELECT version();"
become: true
become_user: postgres
register: pg_connect
changed_when: false
- name: Verifikasi konfigurasi yang dihasilkan valid
command: pg_lsclusters
register: clusters
changed_when: false
failed_when: "'online' not in clusters.stdout"
- name: Verifikasi listen_addresses sesuai konfigurasi
command: >
psql -U postgres -c "SHOW listen_addresses;"
become: true
become_user: postgres
register: listen_addr
changed_when: false
failed_when: postgresql_listen_addresses not in listen_addr.stdout
Pola Test yang Efisien: Idempotency Check #
Molecule secara otomatis menjalankan converge dua kali untuk mengecek idempotency. Pastikan semua task mengembalikan ok di run kedua:
# molecule/default/molecule.yml
provisioner:
name: ansible
config_options:
defaults:
callbacks_enabled: profile_tasks # Lihat task mana yang tidak idempoten
verifier:
name: ansible
enabled: true
# Sequence default Molecule sudah melakukan idempotency check:
# create → converge → idempotency (converge lagi, pastikan 0 changed) → verify → destroy
Jika ada task yang selalu changed di run kedua, itu adalah bug idempotency yang harus diperbaiki.
Layer 3: Integration Test (Menit ke Puluhan Menit) #
Integration test memverifikasi bahwa beberapa role bekerja bersama dengan benar:
# tests/integration/test-stack.yml
---
- name: Integration test: full stack
hosts: test_servers
become: true
roles:
- common
- nginx
- myapp
post_tasks:
- name: Tunggu stack siap
pause:
seconds: 15
- name: Verifikasi halaman utama dapat diakses
uri:
url: "http://localhost:{{ nginx_http_port }}"
status_code: 200
return_content: true
register: homepage
failed_when: "'Welcome' not in homepage.content"
- name: Verifikasi API endpoint
uri:
url: "http://localhost:{{ nginx_http_port }}/api/health"
status_code: 200
- name: Verifikasi log aplikasi tidak ada error
shell: "grep -c ERROR /var/log/myapp/app.log || true"
register: error_count
changed_when: false
failed_when: error_count.stdout | int > 0
Strategi untuk Test Environment #
# Opsi 1: Docker (cepat, tapi tidak 100% mirip production)
# Cocok untuk: unit test role, idempotency check
driver:
name: docker
platforms:
- name: ubuntu22
image: geerlingguy/docker-ubuntu2204-ansible:latest
pre_build_image: true
# Opsi 2: Vagrant (lebih mirip VM nyata, lebih lambat)
# Cocok untuk: integration test yang butuh kernel features
driver:
name: vagrant
platforms:
- name: ubuntu22
box: ubuntu/jammy64
memory: 1024
# Opsi 3: EC2/cloud instance (paling akurat, paling mahal)
# Gunakan hanya untuk test sebelum major release
driver:
name: ec2
platforms:
- name: ubuntu22
image: ami-xxxxx
instance_type: t3.micro
Apa yang TIDAK Perlu Di-Test #
Testing yang berlebihan menciptakan beban maintenance tanpa nilai proporsional:
TIDAK perlu di-test:
✗ Apakah modul apt bekerja — itu tanggung jawab Ansible, bukan kita
✗ Apakah variabel dengan nilai default menggunakan nilai defaultnya
✗ Setiap baris konfigurasi yang dihasilkan template
✗ Behavior yang sudah ditest oleh Molecule idempotency check
PERLU di-test:
✓ Apakah service berjalan dan dapat menerima koneksi
✓ Apakah konfigurasi yang dihasilkan valid secara sintaks
✓ Apakah role idempoten (Molecule handle ini otomatis)
✓ Apakah kombinasi role bekerja bersama (integration test)
✓ Edge case: apa yang terjadi jika role dijalankan di server yang sudah ada datanya
Ringkasan #
- Piramida testing: banyak lint/syntax check (detik) → beberapa Molecule test per role (menit) → sedikit integration test (puluhan menit). Jangan balik piramida ini.
- Molecule secara otomatis melakukan idempotency check (converge dua kali) — pastikan semua task mengembalikan
okdi run kedua, bukanchanged.- Test yang baik memverifikasi efek yang bisa diobservasi: service berjalan, port terbuka, koneksi berhasil — bukan implementasi internal.
- Jangan test behavior Ansible — fokus test pada behavior sistem yang dihasilkan oleh role, bukan pada cara Ansible mencapainya.
- Test environment dengan Docker untuk kecepatan (unit test role), Vagrant untuk akurasi (integration test), cloud instance untuk validasi final sebelum major release.
- Test suite yang tidak di-maintain lebih buruk dari tidak ada test — hapus test yang selalu fail atau tidak pernah menemukan bug daripada membiarkannya menjadi noise.