HeroList, daha karmaşık bileşenlerimizin ikincisi olan üst bileşenlerdir. Uygulamanın Angular versiyonunda, kahramanların bir listesini görüyoruz. Listenin her öğesi, CardContent bileşenimizi ve liste öğesini düzenlemek veya silmek için iki ButtonFooter bileşenini saran bir divdir.
feat/HeroList adlı bir dal oluşturun. src/heroes/ klasörü altında 2 dosya oluşturun; HeroList.cy.tsx, HeroList.tsx. Her zamanki gibi, bileşen işlemesine minimal başlayarak aşağıdakileri dosyalara kopyalayın ve yarn cy:open-ct ile koşucuyu açtıktan sonra testi çalıştırın.
React'te bir liste bileşeni oluştururken, önce tek bir öğeyle başlamak ve ardından listeye geçmek daha kolaydır. div ile başlayacağız. İstediğimiz şema şu şekildedir:
div
CardContent
footer
2 xButtonFooter
Önce CardContent işlemini test ederek başlarız (Kırmızı 1).
CardContent çocuk bileşeni ekledikten sonra test geçer, ancak CardContentten eksik olan ad ve açıklama özellikleri hakkında bir derleyici uyarısı alırız. Şimdilik boş dize ile özellikleri ekleyebiliriz.
Kullanımdan heronun ağdan aldığımız bir veri parçası olduğuna dair bir ipucu alırız. Şimdilik bir hero nesnesi oluşturabilir ve bunu hem teste hem de bileşene kopyalayabiliriz (Düzenleme 2).
cancel ve edit düğmelerinin işlemini kontrol etmek için ButtonFooter bileşeninin çeşitlerinden olan yeni bir test oluşturabiliriz. footerı kontrol etmek ve düğmelerin içinde olduğundan emin olmak isteğe bağlıdır; bunun yerine bileşenleri kontrol edebiliriz. Test maliyeti ve güven arasında bir çağrıdır ve ne kadar test ettiğimize bağlıdır. Bu durumda "footer etiketi hiç değişecek mi?", "within api kullanmak yüksek miktarda iş mi?", "Bu ayrıntıyı test etmekle ne kadar daha fazla güven alırız?" gibi sorular kararımızı belirleyebilir (Kırmızı 3).
TDD zihniyetinde, yeşil testlerimiz olduğunda, ek kaynak kod eklemektense daha fazla test eklemeyi veya yeniden düzenlemeyi tercih ederiz. Silme ve kahramanı seçme olaylarını ele alan başarısız bir test yazalım. Şimdilik console.log'u izlemek için benzer heroes/HeroDetail.cy.tsx testi gibi bir şey olduğundan emin olalım. Önceki bölümlerde yaptığımız gibi beforeEach kancası ve context bloğunu kullanabiliriz (Kırmızı 4).
Şu anda tüm testlerin geçmesi için yapmamız gereken, sırasıyla handleDeleteHero ve handleSelectHero argümanlarıyla dönen console.log'lar içeren işlevler doldurmaktır (Yeşil 4).
HeroDetail bileşeni geliştirilirken veri hakkında yaptığımız şu açıklamayı hatırlayın; "Bileşende sert kodlanmış hero nesnesi yerine, verileri bir prop ile iletebiliriz. Bileşenlerimizi prop'larla veya onları saran şeyle değiştiririz ve şu anda prop daha kolay bir seçenek." Bu yaklaşımla tutarlı kalabilir ve bileşene bir prop iletebiliriz; 2 hero nesnesinden oluşan bir dizi.
Testi değiştirerek başlarız. Bir hero nesnesi yerine, 2 hero nesnesinden oluşan bir heroes dizisi vardır. name ve description için string değerlerini kontrol etmek için, hero yerine heroes[0]'a başvururuz. Test hala başarılı, ancak bileşende henüz heroes adlı bir prop olmadığı için TS hatası var (Kırmızı 5).
Derleyici hatasını ele aldık, ancak bileşende hala sabit kodlu hero nesnesini kullanıyoruz ve heroes özelliği ile bileşene geçirilen veriyi kullanmıyoruz. hero nesnesini kaldırmak, testi başarısız kılar ve hero.name ve hero.description kullanımı için derleyici hataları verir (Kırmızı 6).
Başarısızlıkları geçici olarak ele almak için, hero referansı yerine heroes[0] kullanırız (Yeşil 6).
Bu, şu soruyu sormamıza neden olur; birden fazla liste öğesini nasıl görüntüleriz? Tüm liyi kopyalayıp heroes[1] içinde referanslayarak mı yaparız? Bu işe yarar (deneyin) ama bunun iyi olmadığını biliyoruz çünkü DRY değil ve ölçeklenmiyor.
Daha akıllı bir şekilde bir listeyi işlememiz gerekiyor. React'te, JS'ye benzer şekilde, bunu diziyi / verileri eşlemek suretiyle yapıyoruz. Tek fark, liyi sözdizimiyle sarmak için JSX gösterimini kullanma ihtiyacıdır. Bu {} karakterlerini dolar işareti olmayan bir şablon dizesi ${ } gibi düşünün. Şimdi dizi indekslerini değil, haritanın ürettiği tek heroyu referans alabiliriz; dizinin her indeksine eşleşen. map kafa karıştırıcıysa, döndüren ve dizi oluşturmayan, ancak yeni bir dizi oluşturan forEachin daha iyi bir sürümü gibi düşünün (Düzenleme 6).
İyi bir yeniden düzenleme oldu, render iyi görünüyor, ancak Delete ve Edit tıklamaları için test başarısız oluyor çünkü şimdi onların birden fazlası var ve tıklarken listenin first() örneğini kullanarak hızlı bir ayar yapabiliriz. Verileri de Cypress düzeneği olan bir json dosyasına, ./cypress/fixtures altına taşıyabiliriz. ./cypress/fixtures/heroes.json dosyasını oluşturun ve aşağıdaki içeriği yapıştırın.
[ {"id":"HeroAslaug","name":"Aslaug","description":"warrior queen" }, {"id":"HeroBjorn","name":"Bjorn Ironside","description":"king of 9th century Sweden" }, {"id":"HeroIvar","name":"Ivar the Boneless","description":"commander of the Great Heathen Army" }, {"id":"HeroLagertha","name":"Lagertha the Shieldmaiden","description":"aka Hlaðgerðr" }, {"id":"HeroRagnar","name":"Ragnar Lothbrok","description":"aka Ragnar Sigurdsson" }, {"id":"HeroThora","name":"Thora Town-hart","description":"daughter of Earl Herrauðr of Götaland" }]
Testi bu verileri kullanacak şekilde yeniden düzenleyebiliriz (Düzenleme 6).
data-cy özelliğini bileşene eklerken, key özelliği hakkında önemli bir konuyu da ele alacağız.
JS'in map fonksiyonu ikinci bir argüman olan index alır. Bunu şablon dizesiyle kullanarak ve data-cy seçicileri ile liste üzerindeki n. öğeye erişebiliriz:
React bir listeyi (yeniden) render ederken, key özelliği hangi liste öğelerinin değiştiğini belirlemek için kullanılır. Dokümantasyonda belirtildiği üzere, key olarak index yerine benzersiz bir değer kullanılması önerilir, çünkü index kullanmak performansı olumsuz etkileyebilir.
// not preferred{heroes.map((hero, index) => ( <lidata-cy={`hero-list-item-${index}`} key={index}>}
// preferred, because hero.id is always unique{heroes.map((hero, index) => ( <lidata-cy={`hero-list-item-${index}`} key={hero.id}>}
Testin geçmesi için bileşeni data-cy özelliğiyle değiştireceğiz ve her zaman benzersiz olacak bir değere sahip bir key özelliği ekleyeceğiz (Yeşil 7).
Listeyle uğraşmaya başlamadan önce, tek bir liste öğesi için test yazmaya karar verdik.
Alt bileşen CardContenti render eden bir test yazdık ve testi geçmek için sert kodlanmış prop değerleri kullandık (Kırmızı 1, Yeşil 1, Kırmızı 2, Yeşil 2). Hem bileşende hem de testte durumu taklit etmek için sert kodlanmış veriler kullandık (Düzenleme 2).
Diğer alt bileşen ButtonFooterın render edilmesi için bir test ekledik (Kırmızı 3).
Eksik prop'ları eklemek için TS'nin avantajını kullandık (Yeşil 3).
Delete (silme) ve select (seçme) kahraman etkinliklerinin ele alınması için testler ekledik, console.log üzerinde casusluk yaptık (Kırmızı 4).
İlgili etkinlikleri console.log yapan fonksiyonlar ekledik ve tek öğeyi stillerle tamamladık (Yeşil 4, Düzenleme 4).
Liste
Bileşene bir kahramanlar dizisini prop olarak geçmeye karar verdik ve TS hatası gördük (Kırmızı 5). TS hatasını çözmek için HeroListProps içinde Hero[] tipini kullandık (Yeşil 5).
Testten sert kodlanmış hero nesnesini kaldırdık, bu da başarısızlığa neden oldu (Kırmızı 6), bunun yerine heroes dizisinin ilk indeksini heroes[0] ile belirttik (Yeşil 6).
React'te listeleri render etme kalıbı hakkında düşündük; map kullanarak. Dizinin indeks referansını, her dizi öğesi için hero nesnesi olan map geri çağırma argümanına değiştirdik. Ayrıca, bileşen testinde daha büyük bir liste render etmek için Cypress fixture'ını kullanmaya başladık (Düzenleme 6).
Tüm listenin uzunluğunu doğrulayan bir test ekledik (Kırmızı 7).
React'in listelerde hangi öğelerin güncellendiğini belirlemek ve optimize etmek için kullandığı key özelliği hakkında düşündük. Bileşeni bu bilgiyle değiştirdik (Yeşil 7).
Çıkarımlar
React'te bir liste bileşeni oluştururken, önce tek bir öğeyle başlamak ve ardından listeye geçmek daha kolaydır.
React'te listeleri render ederken ve veriler üzerinde haritalama yaparken, key özelliği için önerilen değer benzersiz olmalıdır, örneğin haritalanan dizinin indeksi yerine hero.id gibi. Ancak indeks, nth öğeyi seçmek için data-cy özelliğinde başvurulacak öğeye yararlıdır.
Cypress' düzeltmeleri json olarak içe aktarılabilir ve testlerde veri yerine kullanılabilir.
TDD zihniyetinde, yeşil testlere sahip olduğumuzda, daha fazla test eklemeyi veya yeniden düzenlemeyi, ek kaynak kodu eklemeye tercih etmek isteriz.
Test etme her zaman maliyet ve güven arasındaki bir çağrıdır ve ne kadar test ettiğimiz değişir. Kararımızı belirleyebilecek bazı sorular şunlardır:
Test edilen kod ne sıklıkla değişir?
Şimdi daha fazla inceleme yapan test kodunu yazmak yüksek miktarda çalışma mı?
Bu ayrıntıyı test ederek ne kadar fazla güven elde ederiz?