Heroes bileşenimiz, rotaları kullanmaya ihtiyaç duyuyor, ancak uygulamamızda bunu henüz ayarlamadık. Şu ana kadar, her bileşen izole bir şekilde tasarlandı. Bu arada, gerçek uygulama genel bir sayfada başlatılır ve kullanıcı bununla çok fazla işlem yapamaz. Bu bölümde react-router ayarlayacağız ve bunu test etmek için e2e kullanacağız.
react-router-dom ve react-router-nativereact-router içindedir. Web uygulaması üzerinde çalıştığımız için react-router-dom'u react-router olarak adlandıracağız.
e2e Kullanımı
Bileşenlerin içinde gezinme bağlantıları olan bileşenlerde, rotalama özelliğinin bir kısmını bileşen testinde test edebiliriz. HeaderBar ve NavBar bileşenlerinde bunların örneklerini uyguladık. Bunun ötesinde, rotayı test etmenin en güvenilir yolu e2e testler kullanmaktır çünkü uygulamanın rotalama özelliklerini ve olası akışlarını tamamen kapsamamızı sağlar.
e2e koşucusunu yarn cy:open-e2e ile başlatıyoruz. Bu komut, localhost:3000 adresinde uygulamaya hizmet veren yarn start komutunu da çalıştırır. Şu anda, spec dosyasını çalıştırırken genel React uygulamasını görüyoruz. Bunu routes-nav.cy.ts olarak yeniden adlandırabiliriz.
Gereksinimimiz, uygulamamızı sunmak, bağlantılara tıklamak, doğru URL'lere gitmek ve ilgili bileşenleri işlemektir.
Henüz Villains bileşenini uygulamadık, şimdilik bu sorun değil.
Yukarıdan aşağıya doğru başlayarak, ilk başarısız testimiz HeaderBar bileşenini işlemek için (Kırmızı 1).
Diğer bileşenleri içeren bir bileşeni test ettiğimizde, çocuk bileşenin kaynağına ve bileşen testine bakmak gibi en iyi bir uygulama vardır. e2e ile uygulamayı test ederken de aynıdır. src/components/HeaderBar.cy.tsx, HeaderBar'ı monte ederken BrowserRouter ile sarmalar, bu da ana uygulamamızın da BrowserRouter'a ihtiyacı olduğu anlamına gelir (Yeşil 1).
NavBar.cy.tsx'ye baktığımızda kahramanlar, kötü adamlar ve hakkında tıklama navigasyonunu zaten kapsadığımızı görüyoruz. Bu testi e2e'de tekrarlamamıza gerek yok. Daha düşük seviyedeki testlerin kapsamını her zaman kontrol edin ve daha yüksek seviyede çaba harcamaktansa çabayı çoğaltmamayı tercih edin, çünkü bu daha fazla maliyet getirebilir ama ekstra güven sağlamayabilir.
e2e veya bileşen testleri kullanıyor olsanız bile, TDD'nin akışı aynıdır; başarısız olan bir şeyle başlayın, çalışacak şekilde en aza indirgeyin ve ardından daha iyi hale getirin. Ana ayrım ölçektir; e2e ile daha küçük artımlı adımlara sahip olmak için daha dikkatli olmamız gerekiyor çünkü uygulamanın büyük ölçekteki etkisi daha yüksek olabilir ve başarısızlıkları teşhis etmeyi daha zor hale getirebilir. Test odaklı tasarımda uygulaması zor olan açık uygulama, bir seferde çok küçük artımlı testler yazmaktır.
Yönlendirme
Mevcut olmayan bir rotayı ziyaret ettiğimizde NotFound bileşenini işleyip işlemediğimizi kontrol eden başarısız bir test yazın (Kırmızı 3).
// cypress/e2e/routes-nav.cy.tsdescribe("e2e sanity", () => {it("should render header bar and nav bar", () => {cy.visit("/");cy.getByCy("header-bar").should("be.visible");cy.getByCy("nav-bar").should("be.visible"); });it("should land on not found when visiting an non-existing route", () => {cy.visit("/route48");cy.getByCy("not-found").should("be.visible"); });});
react-router kullanmak için bileşenimizi bir Routes bileşeniyle sarmalamanız ve içe aktarmanız gerekir. Her bileşen, bir Route bileşenindeki bir element özelliği olur. Bileşeni bir path özelliğine eşleriz. * ise diğer yollara uymayan her şeyin buna yönlendirileceği anlamına gelir (Yeşil 3).
About bileşenini ekleyerek yol ayarını biraz daha ilginç hale getirelim. Aşağıdakileri src/About.tsx'ye kopyalayın.
// src/About.tsximport React from"react";constAbout= () => ( <divdata-cy="about"className="content-container"> <divclassName="content-title-group not-found"> <h2className="title">Tour of Heroes</h2> <p> This project was created to provide a perspective on Test Driven Design using Cypress component and e2e testing to develop a React application. There are many versions of Angular's Tour of Heroes tutorial and John Papa has re-created them in Angular, Vue and React. The 3 apps are consistent in their styles and design decisions. This one inspires from them, uses CCTDD and takes variances along the way. </p> </br> <h2className="title">Live applications by John Papa</h2> <ul> <li> <ahref="https://papa-heroes-angular.azurewebsites.net"> Tour of Heroes with Angular </a> </li> <li> <ahref="https://papa-heroes-react.azurewebsites.net"> Tour of Heroes with React </a> </li> <li> <ahref="https://papa-heroes-vue.azurewebsites.net"> Tour of Heroes with Vue </a> </li> </ul> </div></div>);exportdefault About;
Şimdi yola doğrudan gitmeyi test eden başarısız bir test yazabiliriz. NavBar bileşen testinde tıklama-nav sürümünü zaten yazdık ve bunu e2e'de tekrar etmiyoruz (Kırmızı 4).
// cypress/e2e/routes-nav.cy.tsdescribe("e2e sanity", () => {it("should render header bar and nav bar", () => {cy.visit("/");cy.getByCy("header-bar").should("be.visible");cy.getByCy("nav-bar").should("be.visible"); });it("should land on not found when visiting an non-existing route", () => {cy.visit("/route48");cy.getByCy("not-found").should("be.visible"); });it("should direct-navigate to about", () => {cy.visit("/about");cy.getByCy("about").contains("CCTDD"); });});
About bileşenini /about yoluna ayarlayarak testi geçerli hale getiriyoruz (Yeşil 4).
test eklemeyi düşünmek istiyoruz. URL kontrollerini ekleyerek testleri, bir url'ye yönlendirirken bileşen render'ının yanı sıra destekleyebiliriz (Düzenleme 4).
// cypress/e2e/routes-nav.cy.tsdescribe("e2e sanity", () => {it("should render header bar and nav bar", () => {cy.visit("/");cy.getByCy("header-bar").should("be.visible");cy.getByCy("nav-bar").should("be.visible"); });it("should land on not found when visiting an non-existing route", () => {constroute="/route48";cy.visit(route);cy.location('pathname').should('eq', route);cy.getByCy("not-found").should("be.visible"); });it("should direct-navigate to about", () => {constroute="/about";cy.visit(route);cy.location('pathname').should('eq' route);cy.getByCy("about").contains("CCTDD"); });});
Uygulamamız için varsayılan URL'nin ne olması gerektiği konusunda bir soru sormak istiyoruz. En karmaşık bileşen Heroes olduğundan, bu uygun bir seçimdir. Boş bir yola gidildiğinde /heroes yoluna yönlendirilmek ve Heroes bileşenini görüntülemek istiyoruz. Bu ihtiyaca yönelik başarısız bir test ekleyelim (Kırmızı 5).
// cypress/e2e/routes-nav.cy.tsdescribe("e2e sanity", () => {it("should render header bar and nav bar", () => {cy.visit("/");cy.getByCy("header-bar").should("be.visible");cy.getByCy("nav-bar").should("be.visible");cy.location('pathname').should('eq'"heroes"); });it("should land on not found when visiting an non-existing route", () => {constroute="/route48";cy.visit(route);cy.location('pathname').should('eq' route);cy.getByCy("not-found").should("be.visible"); });it("should direct-navigate to about", () => {constroute="/about";cy.visit(route);cy.location('pathname').should('eq' route);cy.getByCy("about").contains("CCTDD"); });});
react-routerda bu özelliği kullanmanın yolu Navigate bileşenini kullanmaktır. Şimdi, yönlendirilebilecek /heroes yoluna da ihtiyacımız var (Yeşil 5).
İlk testi, HeaderBar ve NavBarın tüm yönlendirme testlerinde doğru olduğunu kontrol eden testi değiştirebiliriz. Burada, yönlendirmeye yönelik yeni bir test yazmak yerine testi değiştirmek tercih edilir. Mevcut testi değiştirmek için fırsatlar arayın, yeni özellikler için kısmen yinelenen testler yazmak yerine. Bir test açısından önemli olan şey, bir testin başlangıç durumudur; bu duruma ulaşmak ortaksa, o zaman bu test iyileştirmesi için bir fırsattır ve kısmi test çoğaltma yerine. Ayrıca /heroes rotası için doğrudan gezinme işlevselliğini kontrol eden yeni bir test ekleyebiliriz (Düzenleme 5).
// cypress/e2e/routes-nav.cy.tsdescribe("Routes and navigation", () => {it("should land on baseUrl, redirect to /heroes", () => {cy.visit("/");cy.getByCy("header-bar").should("be.visible");cy.getByCy("nav-bar").should("be.visible");cy.location('pathname').should('eq'"/heroes");cy.getByCy("heroes").should("be.visible"); });it("should direct-navigate to /heroes", () => {constroute="/heroes";cy.visit(route);cy.location('pathname').should('eq' route);cy.getByCy("heroes").should("be.visible"); });it("should land on not found when visiting an non-existing route", () => {constroute="/route48";cy.visit(route);cy.location('pathname').should('eq' route);cy.getByCy("not-found").should("be.visible"); });it("should direct-navigate to about", () => {constroute="/about";cy.visit(route);cy.location('pathname').should('eq' route);cy.getByCy("about").contains("CCTDD"); });});
Bu noktada başka hangi testleri düşünebiliriz? Rota geçmişi hakkında ne dersiniz? Bunun için düşük maliyetli ve güvenilir bir e2e testi ile kapsayabileceğimiz bir test ekleyebiliriz. Testi, kahramanlar -> kötü adamlar -> hakkında'dan farklı bir rota sırası kullanarak daha ilginç hale getirebiliriz (Düzenleme 5).
// cypress/e2e/routes-nav.cy.tsdescribe("e2e sanity", () => {it("should land on baseUrl, redirect to /heroes", () => {cy.visit("/");cy.getByCy("header-bar").should("be.visible");cy.getByCy("nav-bar").should("be.visible");cy.location('pathname').should('eq'"/heroes");cy.getByCy("heroes").should("be.visible"); });it("should direct-navigate to /heroes", () => {constroute="/heroes";cy.visit(route);cy.location('pathname').should('eq' route);cy.getByCy("heroes").should("be.visible"); });it("should land on not found when visiting an non-existing route", () => {constroute="/route48";cy.visit(route);cy.location('pathname').should('eq' route);cy.getByCy("not-found").should("be.visible"); });it("should direct-navigate to about", () => {constroute="/about";cy.visit(route);cy.location('pathname').should('eq' route);cy.getByCy("about").contains("CCTDD"); });it("should cover route history with browser back and forward", () => {cy.visit("/");constroutes= ["villains","heroes","about"];cy.wrap(routes).each((route:string) =>cy.get(`[href="/${route}"]`).click() );constlastIndex=routes.length-1;cy.location('pathname').should('eq' routes[lastIndex]);cy.go("back");cy.location('pathname').should('eq' routes[lastIndex -1]);cy.go("back");cy.location('pathname').should('eq' routes[lastIndex -2]);cy.go("forward").go("forward");cy.location('pathname').should('eq' routes[lastIndex]); });});
Bileşen testi kullanma
Doğrudan gezinme ve yönlendirme ile ilgili testler konusunda e2e ile çok güveniyoruz. Ayrıca src/components/NotFound.cy.tsx dosyasında tıklama ile gezinmeyi de ele aldık. Bir bileşen testinde, url bağlanma sırasında mevcut değildir ve cy.visit kullanamayız. Ancak tıklama ile gezinme kullanabiliriz. App.cy.tsx dosyasını bu şekilde güncelleyebiliriz. Cypress koşucusundan bileşen testine geçin veya yarn cy:open-ct ile başlatın.
Bağlanma işlemi sırasında url'nin belirsiz olduğunu cy.getByCy('not-found').should('be.visible') kullanarak kontrol ediyoruz. Geri kalan test, src/components/NotFound.cy.tsx dosyasından kopyalanan ve yapıştırılan bir kısımdır. Bunun yerine, App bileşeninin alt bileşenlerinin render'ını kontrol edebiliriz.
Bu test, mevcut e2e ve bileşen testlerinin üzerinde fazladan bir güven sağlamaz çünkü ekstra bir şey yapmaz. En iyisi, akıl sağlığı testi olarak hizmet edebilir. Şimdilik Cypress bileşen testini React Testing Library ile karşılaştıran yan projede saklayacağız. İşte aynı testin başlangıç RTL kopyası.
// src/App.test.tsximport { render, screen } from"@testing-library/react";import userEvent from"@testing-library/user-event";import App from"./App";test("renders tour of heroes",async () => {render(<App />);awaituserEvent.click(screen.getByText("About"));expect(screen.getByTestId("about")).toBeVisible();awaituserEvent.click(screen.getByText("Heroes"));expect(screen.getByTestId("heroes")).toBeVisible();});// CT vs RTL: src/App.cy.tsx
src/setupTests.ts dosyasını güncelleyin ve varsayılan test kimliği seçiciyi data-cy olarak yeniden yazın.
/about rotaları için doğrudan bir navigasyon testi ekledik (Kırmızı 4).
About bileşeni için rotayı ayarladık (Yeşil 4).
Uygulamanın başlangıç yönlendirmesini /heroes'tan (Kırmızı 5) kontrol etmek için bir test ekledik.
Navigate ile rotaların ayarını geliştirdik (Yeşil 5).
Doğrudan /heroes'a yönlendirmeyi kontrol etmek için bir test ekledik ve rota geçmişini test etmek için başka bir test ekledik (Düzenleme 5).
App.cy.tsx adlı testi inceledik ve test çoğaltma konusunu tartıştık. Testin, başka bir bileşen testi veya e2e testinden başka bir şey yapmadığı halde, mantıklı olduğuna karar verdik. Başka bir neden ise, CT ile RTL arasındaki 1: 1 karşılaştırmalarını incelemeye başlamaktı. App RTL testini, App mantıklı bileşen testini yansıtacak şekilde güncelledik.
Çıkarılacak Dersler
E2e testi, uygulamanın yönlendirme özelliklerini ve olası akışları daha iyi bir şekilde kapsamamıza olanak tanır.
E2e veya bileşen testleri kullanıyor olun, TDD'nin temel fikri aynıdır; başarısız olan bir şeyle başlayın, çalışmasını sağlamak için minimumu yapın ve ardından daha iyi hale getirin.
Test odaklı tasarımda uygulamaya açık ama zor olan uygulama, bir seferde çok küçük artımlı testler yazmaktır. Değişikliklerin daha yüksek etki yarıçapı nedeniyle, e2e testlerle daha küçük artışları daha dikkatli değerlendirin.
Başka bileşenleri içeren bir bileşeni test ettiğimizde, çocuk bileşen kaynağına ve bileşen testine bakın. Aynı kural e2e için de geçerlidir. Daha düşük seviyeli testlerin kapsamını her zaman kontrol edin ve çaba sarf etmeyi yüksek seviyede tekrarlamaktan kaçının, çünkü ekstra maliyeti olacaktır ancak ekstra güvence sağlamayabilir.
Geçerli testlerimiz olduğunda, daha fazla kaynak kodu eklemekten önce refactor yapmayı veya daha fazla test eklemeyi tercih ederiz.
Zaten mevcut olan testi değiştirmek için fırsatlar arayın, yeni özellikler için kısmen yinelenen testler yazmak yerine. Bir test açısından önemli olan şey, bir testin başlangıç durumudur; bu duruma ulaşmak yaygın ise, bu, testin iyileştirilmesi veya kısmi test çoğaltması açısından bir fırsattır.