React Entegrasyon Testiyle Kod Bakımını Arttırın
Yayınlanan: 2022-03-11Entegrasyon testleri, testlerin maliyeti ve değeri arasında tatlı bir noktadır. Bileşen birim testleri yerine veya bunlara ek olarak reaksiyon testi kitaplığı yardımıyla bir React uygulaması için entegrasyon testleri yazmak, geliştirme hızını bozmadan kodun sürdürülebilirliğini artırabilir.
Devam etmeden önce bir başlangıç yapmak isterseniz, burada React uygulama entegrasyon testleri için tepki-test-kütüphanesinin nasıl kullanılacağına dair bir örnek görebilirsiniz.
Neden Entegrasyon Testine Yatırım Yapmalısınız?
"Entegrasyon testleri, güven ve hız/gider arasındaki dengeler üzerinde büyük bir denge kurar. Bu nedenle, çabanızın çoğunu (hepsini değil, unutmayın) orada harcamanız tavsiye edilir."
– Yazma testlerinde Kent C. Dodds. Çok fazla değil. Çoğunlukla entegrasyon.
React “enzimlerini” test etmek için genellikle popüler bir kitaplık kullanarak, React bileşenleri için birim testleri yazmak yaygın bir uygulamadır; özellikle, "sığ" yöntemi. Bu yaklaşım, bileşenleri uygulamanın geri kalanından ayrı olarak test etmemize olanak tanır. Bununla birlikte, React uygulamaları yazmak tamamen bileşenleri oluşturmakla ilgili olduğundan, tek başına birim testleri, uygulamanın hatasız olmasını sağlamaz.
Örneğin, bir bileşenin kabul edilen özelliklerini değiştirmek ve ilgili birim testlerini güncellemek, başka bir bileşen uygun şekilde güncellenmediyse uygulama hala bozulabilirken tüm testlerin geçmesine neden olabilir.
Entegrasyon testleri, bileşenlerin bileşiminin istenen UX ile sonuçlanmasını sağladığından, bir React uygulamasında değişiklik yaparken gönül rahatlığının korunmasına yardımcı olabilir.
React Uygulama Entegrasyon Testleri için Gereksinimler
React geliştiricilerinin entegrasyon testleri yazarken yapmak istedikleri şeylerden bazıları şunlardır:
- Uygulama kullanım durumlarını kullanıcının bakış açısından test edin. Kullanıcılar bir web sayfasındaki bilgilere erişir ve mevcut kontrollerle etkileşime girer.
- API kullanılabilirliğine ve testleri geçme/başarısız olma durumuna bağlı olmamak için API çağrılarını taklit edin.
- Test ortamında bulunmadıkları için tarayıcı API'lerini (örneğin, yerel depolama) taklit edin.
- React DOM durumunu onaylayın (tarayıcı DOM veya yerel bir mobil ortam).
Şimdi, React uygulama entegrasyon testleri yazarken kaçınmamız gereken bazı şeyler için:
- Uygulama ayrıntılarını test edin. Uygulama değişiklikleri, yalnızca gerçekten bir hata ortaya çıkardılarsa bir testi kırmalıdır.
- Çok fazla alay et. Uygulamanın tüm bölümlerinin birlikte nasıl çalıştığını test etmek istiyoruz.
- Sığ işleme. Uygulamadaki tüm bileşenlerin bileşimini en küçük bileşene kadar test etmek istiyoruz.
Neden React-test-kütüphanesini Seçmelisiniz?
Ana yol gösterici ilkesi, React bileşenlerinin gerçek bir insan tarafından nasıl kullanıldığına benzer bir şekilde test edilmesine izin vermek olduğundan, yukarıda belirtilen gereksinimler tepki testi kitaplığını harika bir seçim haline getirir.
Kitaplık, isteğe bağlı tamamlayıcı kitaplıklarıyla birlikte, DOM ile etkileşime giren ve durumu hakkında iddiada bulunan testler yazmamıza olanak tanır.
Örnek Uygulama Kurulumu
Örnek entegrasyon testleri yazacağımız uygulama basit bir senaryo uyguluyor:
- Kullanıcı bir GitHub kullanıcı adı girer.
- Uygulama, girilen kullanıcı adıyla ilişkili genel havuzların bir listesini görüntüler.
Yukarıdaki işlevselliğin nasıl uygulandığı, entegrasyon testi açısından alakasız olmalıdır. Ancak, gerçek dünyadaki uygulamalara yakın olmak için uygulama, ortak React modellerini takip eder, dolayısıyla uygulama:
- Tek sayfalık bir uygulamadır (SPA).
- API istekleri yapar.
- Küresel devlet yönetimine sahiptir.
- Uluslararasılaşmayı destekler.
- Bir React bileşen kitaplığı kullanır.
Uygulama uygulaması için kaynak kodu burada bulunabilir.
Entegrasyon Testleri Yazma
Bağımlılıkları yükleme
İplik ile:
yarn add --dev jest @testing-library/react @testing-library/user-event jest-dom nock
Veya npm ile:
npm i -D jest @testing-library/react @testing-library/user-event jest-dom nock
Entegrasyon Test Paketi Dosyası Oluşturma
Uygulamamızın ./test
klasöründe viewGitHubRepositoriesByUsername.spec.js
adında bir dosya oluşturacağız. Jest otomatik olarak alacaktır.
Test Dosyasındaki Bağımlılıkları İçe Aktarma
import React from 'react'; // so that we can use JSX syntax import { render, cleanup, waitForElement } from '@testing-library/react'; // testing helpers import userEvent from '@testing-library/user-event' // testing helpers for imitating user events import 'jest-dom/extend-expect'; // to extend Jest's expect with DOM assertions import nock from 'nock'; // to mock github API import { FAKE_USERNAME_WITH_REPOS, FAKE_USERNAME_WITHOUT_REPOS, FAKE_BAD_USERNAME, REPOS_LIST } from './fixtures/github'; // test data to use in a mock API import './helpers/initTestLocalization'; // to configure i18n for tests import App from '../App'; // the app that we are going to test
Test Paketini Ayarlama
describe('view GitHub repositories by username', () => { beforeAll(() => { nock('https://api.github.com') .persist() .get(`/users/${FAKE_USERNAME_WITH_REPOS}/repos`) .query(true) .reply(200, REPOS_LIST); }); afterEach(cleanup); describe('when GitHub user has public repositories', () => { it('user can view the list of public repositories for entered GitHub username', async () => { // arrange // act // assert }); }); describe('when GitHub user has no public repositories', () => { it('user is presented with a message that there are no public repositories for entered GitHub username', async () => { // arrange // act // assert }); }); describe('when GitHub user does not exist', () => { it('user is presented with an error message', async () => { // arrange // act // assert }); }); });
Notlar:
- Tüm testlerden önce, belirli bir kullanıcı adıyla çağrıldığında depoların bir listesini döndürmek için GitHub API ile alay edin.
- Her testten sonra, her testin temiz bir noktadan başlaması için test React DOM'yi temizleyin.
-
describe
blokları, entegrasyon testi kullanım durumunu ve akış varyasyonlarını belirtir. - Test ettiğimiz akış varyasyonları:
- Kullanıcı, genel GitHub depolarıyla ilişkilendirilmiş geçerli bir kullanıcı adı girer.
- Kullanıcı, ilişkili genel GitHub deposu olmayan geçerli bir kullanıcı adı girer.
- Kullanıcı, GitHub'da mevcut olmayan bir kullanıcı adı girer.
- test ettikleri kullanım durumunda eşzamansız adım
it
, eşzamansız geri arama kullanımını engeller.
İlk Akış Testini Yazma
İlk olarak, uygulamanın oluşturulması gerekiyor.
const { getByText, getByPlaceholderText, queryByText } = render(<App />);
@testing-library/react
modülünden içe aktarılan render
yöntemi, uygulamayı React DOM testinde işler ve işlenmiş uygulama kapsayıcısına bağlı DOM sorgularını döndürür. Bu sorgular, etkileşimde bulunmak ve üzerinde iddiada bulunmak için DOM öğelerini bulmak için kullanılır.
Şimdi, test edilen akışın ilk adımı olarak, kullanıcıya bir kullanıcı adı alanı sunulur ve içine bir kullanıcı adı dizesi yazar.
userEvent.type(getByPlaceholderText('userSelection.usernamePlaceholder'), FAKE_USERNAME_WITH_REPOS);
İçe aktarılan @testing-library/user-event
modülündeki userEvent
yardımcısı, metin alanına metin yazarken kullanıcının davranışını taklit eden bir type
yöntemine sahiptir. İki parametre kabul eder: girişi kabul eden DOM öğesi ve kullanıcının yazdığı dize.

Kullanıcılar genellikle DOM öğelerini kendileriyle ilişkili metne göre bulur. Giriş durumunda, ya etiket metni ya da yer tutucu metindir. Daha önce render
döndürülen getByPlaceholderText
sorgu yöntemi, DOM öğesini yer tutucu metne göre bulmamızı sağlar.
Metnin kendisinin genellikle değişmesi muhtemel olduğundan, gerçek yerelleştirme değerlerine güvenmemenin ve bunun yerine yerelleştirme modülünü değeri olarak bir yerelleştirme öğesi anahtarı döndürecek şekilde yapılandırmanın en iyisi olduğunu unutmayın.
Örneğin, "en-US" yerelleştirmesi normalde userSelection.usernamePlaceholder
anahtarının değeri olarak Enter GitHub username
döndürdüğünde, testlerde bunun userSelection.usernamePlaceholder
değerini döndürmesini istiyoruz.
Kullanıcı bir alana metin yazdığında, metin alanı değerinin güncellendiğini görmelidir.
expect(getByPlaceholderText('userSelection.usernamePlaceholder')).toHaveAttribute('value', FAKE_USERNAME_WITH_REPOS);
Akışta bir sonraki adımda, kullanıcı gönder düğmesine tıklar ve depoların listesini görmeyi bekler.
userEvent.click(getByText('userSelection.submitButtonText').closest('button')); getByText('repositories.header');
userEvent.click
yöntemi, kullanıcının bir DOM öğesine tıklamasını taklit ederken, getByText
sorgusu içerdiği metne göre bir DOM öğesini bulur. En closest
değiştirici, doğru türden öğeyi seçmemizi sağlar.
Not: Entegrasyon testlerinde, adımlar genellikle hem act
hem de assert
rollerine hizmet eder. Örneğin, kullanıcının bir butona tıklayarak tıklayabileceğini iddia ediyoruz.
Bir önceki adımda, kullanıcının uygulamanın depolar listesi bölümünü gördüğünü iddia etmiştik. Şimdi, GitHub'dan depoların listesini getirmenin biraz zaman alabileceğinden, kullanıcının alma işleminin devam ettiğine dair bir gösterge gördüğünü belirtmemiz gerekiyor. Ayrıca, veri havuzu listesi getirilmeye devam ederken uygulamanın kullanıcıya, girilen kullanıcı adıyla ilişkili hiçbir veri havuzu olmadığını söylemediğinden emin olmak istiyoruz.
getByText('repositories.loadingText'); expect(queryByText('repositories.empty')).toBeNull();
getBy
sorgu önekinin, DOM öğesinin bulunabileceğini iddia etmek için kullanıldığını ve queryBy
sorgu önekinin karşıt iddia için kullanışlı olduğunu unutmayın. Ayrıca, hiçbir öğe bulunamazsa, queryBy
bir hata döndürmez.
Ardından, uygulamanın depoları almayı bitirdiğinden ve bunları kullanıcıya gösterdiğinden emin olmak istiyoruz.
await waitForElement(() => REPOS_LIST.reduce((elementsToWaitFor, repository) => { elementsToWaitFor.push(getByText(repository.name)); elementsToWaitFor.push(getByText(repository.description)); return elementsToWaitFor; }, []));
waitForElement
zaman uyumsuz yöntemi, yöntem parametresi olarak sağlanan onayın doğru olmasını sağlayacak bir DOM güncellemesini beklemek için kullanılır. Bu durumda, uygulamanın, alaylı GitHub API'si tarafından döndürülen her depo için adını ve açıklamasını görüntülediğini iddia ediyoruz.
Son olarak, uygulama artık depoların getirildiğini gösteren bir gösterge göstermemeli ve bir hata mesajı göstermemelidir.
expect(queryByText('repositories.loadingText')).toBeNull(); expect(queryByText('repositories.error')).toBeNull();
Ortaya çıkan React entegrasyon testimiz şöyle görünür:
it('user can view the list of public repositories for entered GitHub username', async () => { const { getByText, getByPlaceholderText, queryByText } = render(<App />); userEvent.type(getByPlaceholderText('userSelection.usernamePlaceholder'), FAKE_USERNAME_WITH_REPOS); expect(getByPlaceholderText('userSelection.usernamePlaceholder')).toHaveAttribute('value', FAKE_USERNAME_WITH_REPOS); userEvent.click(getByText('userSelection.submitButtonText').closest('button')); getByText('repositories.header'); getByText('repositories.loadingText'); expect(queryByText('repositories.empty')).toBeNull(); await waitForElement(() => REPOS_LIST.reduce((elementsToWaitFor, repository) => { elementsToWaitFor.push(getByText(repository.name)); elementsToWaitFor.push(getByText(repository.description)); return elementsToWaitFor; }, [])); expect(queryByText('repositories.loadingText')).toBeNull(); expect(queryByText('repositories.error')).toBeNull(); });
Alternatif Akış Testleri
Kullanıcı, ilişkili ortak depoları olmayan bir GitHub kullanıcı adı girdiğinde, uygulama uygun bir mesaj görüntüler.
describe('when GitHub user has no public repositories', () => { it('user is presented with a message that there are no public repositories for entered GitHub username', async () => { const { getByText, getByPlaceholderText, queryByText } = render(<App />); userEvent.type(getByPlaceholderText('userSelection.usernamePlaceholder'), FAKE_USERNAME_WITHOUT_REPOS); expect(getByPlaceholderText('userSelection.usernamePlaceholder')).toHaveAttribute('value', FAKE_USERNAME_WITHOUT_REPOS); userEvent.click(getByText('userSelection.submitButtonText').closest('button')); getByText('repositories.header'); getByText('repositories.loadingText'); expect(queryByText('repositories.empty')).toBeNull(); await waitForElement(() => getByText('repositories.empty')); expect(queryByText('repositories.error')).toBeNull(); }); });
Kullanıcı var olmayan bir GitHub kullanıcı adı girdiğinde uygulama bir hata mesajı görüntüler.
describe('when GitHub user does not exist', () => { it('user is presented with an error message', async () => { const { getByText, getByPlaceholderText, queryByText } = render(<App />); userEvent.type(getByPlaceholderText('userSelection.usernamePlaceholder'), FAKE_BAD_USERNAME); expect(getByPlaceholderText('userSelection.usernamePlaceholder')).toHaveAttribute('value', FAKE_BAD_USERNAME); userEvent.click(getByText('userSelection.submitButtonText').closest('button')); getByText('repositories.header'); getByText('repositories.loadingText'); expect(queryByText('repositories.empty')).toBeNull(); await waitForElement(() => getByText('repositories.error')); expect(queryByText('repositories.empty')).toBeNull(); }); });
Neden React Entegrasyon Testleri Rock
Entegrasyon testi, React uygulamaları için gerçekten tatlı bir nokta sunar. Bu testler, hataları yakalamaya ve TDD yaklaşımını kullanmaya yardımcı olurken aynı zamanda uygulama değiştiğinde bakım gerektirmezler.
Bu makalede gösterilen React-testing-library, kullanıcının yaptığı gibi uygulamayla etkileşim kurmanıza ve uygulama durumunu ve davranışını kullanıcının bakış açısından doğrulamanıza olanak tanıdığından, React entegrasyon testleri yazmak için harika bir araçtır.
Umarım burada verilen örnekler, yeni ve mevcut React projelerinde entegrasyon testleri yazmaya başlamanıza yardımcı olur. Uygulama uygulamasını içeren tam örnek kod GitHub'ımda bulunabilir.