İçeriğe geç
Muhammet Şafak
Araçlar & Teknolojiler 4 dk okuma

Vue bileşenlerini test etmek

Vue bileşenlerinin arayüz davranışını otomatik olarak doğrulamanın pratik yolu: Vue Test Utils ile gerçek senaryolar üzerinden test yazımı.


PHP tarafında PHPUnit ile test yazma alışkanlığını oturtmuştum. Ama frontend bileşenlerine gelince test konusunda gecikmeli kaldım. “Arayüzü test etmek zor” diyordum — bir ölçüde doğru, ama bu gecikmede tembellik de payı vardı. Vue Test Utils’i düzgünce kullanmaya başladığımda görüyorum ki Vue bileşenlerini test etmek, düşündüğümden çok daha pratik.

Bu yazıda “neden test yazmalısın?” sorusundan kaçınıp doğrudan “nasıl yapılır?” üzerine duracağım.

Araç seti

Vue 2 için standart test kombinasyonu: Vue Test Utils + Jest. Vue Test Utils, bileşenleri render edip etkileşime girmenizi, çıktıyı sorgulamanızı sağlayan resmi yardımcı kütüphane. Jest ise test çalıştırıcı ve assertion kütüphanesi.

Kurulum:

npm install --save-dev @vue/test-utils jest vue-jest babel-jest

jest.config.js içinde Vue dosyalarını vue-jest ile transform etmek gerekiyor.

İlk bileşen testi

Basit bir Counter.vue bileşeni düşünelim:

<template>
  <div>
    <span data-testid="count">{{ count }}</span>
    <button @click="increment">Artır</button>
  </div>
</template>

<script>
export default {
  data() {
    return { count: 0 };
  },
  methods: {
    increment() {
      this.count++;
    }
  }
};
</script>

Testi:

import { shallowMount } from '@vue/test-utils';
import Counter from '@/components/Counter.vue';

describe('Counter', () => {
  it('başlangıçta 0 gösterir', () => {
    const wrapper = shallowMount(Counter);
    expect(wrapper.find('[data-testid="count"]').text()).toBe('0');
  });

  it('butona tıklayınca değer artar', async () => {
    const wrapper = shallowMount(Counter);
    await wrapper.find('button').trigger('click');
    expect(wrapper.find('[data-testid="count"]').text()).toBe('1');
  });
});

shallowMount bileşeni shallow render ediyor: alt bileşenler stub olarak geçiyor. mount ise tam ağacı render eder. Yalıtım için shallowMount genellikle daha tercih edilebilir.

data-testid attribute’u kullanmak bir alışkanlık edindim. CSS sınıfları veya element seçiciler değişince testler kırılıyor; data-testid ise yalnızca test için var ve kararlı kalıyor.

Props ve emit testleri

Bir bileşenin props’a göre nasıl davrandığını ve hangi event’leri yaydığını test etmek:

import { shallowMount } from '@vue/test-utils';
import ConfirmDialog from '@/components/ConfirmDialog.vue';

describe('ConfirmDialog', () => {
  it('message prop\'unu gösterir', () => {
    const wrapper = shallowMount(ConfirmDialog, {
      propsData: { message: 'Silmek istediğinizden emin misiniz?' }
    });
    expect(wrapper.text()).toContain('Silmek istediğinizden emin misiniz?');
  });

  it('onayla butonuna tıklayınca confirm eventi yayar', async () => {
    const wrapper = shallowMount(ConfirmDialog, {
      propsData: { message: 'Test' }
    });
    await wrapper.find('[data-testid="confirm-btn"]').trigger('click');
    expect(wrapper.emitted('confirm')).toBeTruthy();
  });
});

Emit testleri backend’e kıyasla tuhaf hissettiriyor çünkü event’in alıcısını test etmiyorsunuz, sadece “yayıldı mı?” diye bakıyorsunuz. Ama bu doğru yaklaşım: bileşenin sorumluluğu emit etmek, ne yapılacağına ebeveyn bileşen karar veriyor. Kapsam doğru tutulunca test de net oluyor.

Vuex store ile bileşen testi

Store’a bağlı bileşenler biraz daha kurulum gerektiriyor. İki yaklaşım var: gerçek store oluşturmak veya store’u mock’lamak. Yalıtılmış birim testleri için store taklit tercih ediyorum:

import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import UserList from '@/components/UserList.vue';

const localVue = createLocalVue();
localVue.use(Vuex);

describe('UserList', () => {
  let store;

  beforeEach(() => {
    store = new Vuex.Store({
      state: {
        users: [
          { id: 1, name: 'Ali' },
          { id: 2, name: 'Ayşe' }
        ]
      }
    });
  });

  it('store\'daki kullanıcıları listeler', () => {
    const wrapper = shallowMount(UserList, { store, localVue });
    expect(wrapper.findAll('[data-testid="user-item"]')).toHaveLength(2);
  });
});

Burada dikkat ettiğim bir şey: her test için beforeEach içinde store’u yeniden oluşturmak. Paylaşılan store, bir testin diğerini kirletmesine yol açabiliyor. PHPUnit’te her testin temiz bir veritabanıyla başlaması gibi, burada da her test temiz bir store’la başlıyor.

Ne test etmeli, neyi değil?

Her satırı test etmek zorunda değilsiniz. Değerli testler şunlar:

  • Bileşenin kullanıcı girdisine verdiği yanıt.
  • Props ile render’ın değişimi.
  • Kritik koşul dalları (conditional rendering).
  • Emit edilen olaylar.

CSS stillerini veya animasyonları test etmek genellikle maliyetine değmiyor. Bileşenin davranışını test etmek, görünümünü değil.

Backend’de “bu endpoint 200 mi, 403 mü döndürüyor?” sorusunun cevabını test edersiniz; frontend’de de “bu butona tıklayınca doğru şey oldu mu?” sorusunun cevabını test ediyorsunuz. Çerçeve farklı, mantık aynı.

Frontend testleri yazmak, backend testleri kadar yerleşik bir alışkanlık haline gelmeli. Bileşeni elle tıklayıp “çalışıyor” demek yeterli değil — özellikle bileşen bir başkasına bağımlıysa ve değişiklik yapacaksanız. İlk kez refaktör yaparken testlerin tuttuğu güvenlik ağını görünce bu konudaki gecikmeme şaşırdım.

Bir de şunu fark ettim: test yazarken bileşeni API’sinden bakıyorsunuz — prop’lar neler, hangi eventleri yayıyor, kullanıcı ne görüyor. Bu bakış açısı, bileşenin dışarıdan nasıl kullanılacağını düşünmek demek. İyi yazılmış test senaryoları aynı zamanda iyi tasarlanmış bir bileşen arayüzüne işaret ediyor. Test yazmanın zorladığı netlik, tasarımı da düzeltiyor. Bir bileşeni test etmek zorlaşıyorsa bu çoğunlukla bileşenin çok fazla sorumluluk üstlendiğinin işareti; testi kolaylaştırmak için bileşeni küçültmek hem test senaryosunu hem de asıl kodu daha iyi hale getiriyor.

Etiketler: #Vue.js#Test
Paylaş:

İlgili Yazılar

Sitede Ara

Yazı, proje ve sayfalarda arama yapmak için yazmaya başlayın.

Esc ile kapat Pagefind ile güçlendirildi