Cypress Testlerinizi Yapılandırmak, Düzenlemek ve Birleştirmek için Fikirler #frontend@twiliosendgrid

Yayınlanan: 2020-12-15

Twilio SendGrid'de farklı web uygulamaları ve ön uç ekipleri arasında birçok Cypress testi yazdık. Testlerimiz birçok özellik ve sayfaya ölçeklenirken, bazı yararlı yapılandırma seçeneklerine rastladık ve bu arada artan sayıda dosyamızı, sayfamızın öğelerine yönelik seçicileri ve zaman aşımı değerlerimizi daha iyi korumanın yollarını geliştirdik.

Cypress ile ilgili kendi şeylerinizi yapılandırmak, organize etmek ve birleştirmek için bu ipuçlarını ve fikirleri size göstermeyi amaçlıyoruz, bu yüzden onlarla sizin ve ekibiniz için en iyi olanı yapmaktan çekinmeyin.

Bu blog yazısı, Cypress testleri hakkında biraz bilgi sahibi olduğunuzu ve Cypress testlerinizi daha iyi sürdürme ve iyileştirme konusunda fikirler aradığınızı varsayar. Ancak, ayrı ortamlara karşı Cypress testleri yazmak için yararlı bulabileceğiniz ortak işlevler, iddialar ve kalıplar hakkında daha fazla bilgi edinmek istiyorsanız, bunun yerine bu bin fit genel bakış blog gönderisine göz atabilirsiniz.

Aksi takdirde, devam edelim ve önce Cypress için bazı yapılandırma ipuçlarında size yol gösterelim.

cypress.json'unuzu yapılandırma

Cypress.json dosyası, Cypress komutlarınız için temel zaman aşımları, ortam değişkenleri ve diğer özellikler gibi cypress.json testleriniz için tüm yapılandırmayı ayarlayabileceğiniz yerdir .

Yapılandırmanızda denemeniz için bazı ipuçları:

  • Temel komut zaman aşımlarınızda ince ayar yapın, böylece Cypress komutlarınıza her zaman { timeout: timeoutInMs } eklemek zorunda kalmazsınız. "defaultCommandTimeout", "requestTimeout" ve "responseTimeout" gibi ayarlar için doğru dengeyi bulana kadar sayılarla uğraşın.
  • Testiniz iframe'leri içeriyorsa, uygulamanızda çapraz kaynaklı iframe'lere erişebilmek için büyük olasılıkla "chromeWebSecurity"yi false olarak ayarlamanız gerekir.
  • Hızı artırmak ve istenmeyen olayları tetiklememek için Cypress testlerinizi yürütürken üçüncü taraf pazarlama, analiz ve günlük komut dosyalarını engellemeyi deneyin. Üçüncü taraf ana bilgisayar yollarıyla eşleşen bir dizi dize globunu alarak “kara listeHosts” özelliğiyle (veya Cypress 5.0.0'da “blockHosts” özelliğiyle) kolayca bir reddetme listesi oluşturabilirsiniz.
  • İşlerin daha kolay görülmesini sağlamak için Cypress GUI'yi açtığınızda varsayılan görünüm penceresi boyutlarını “viewportWidth” ve “viewportHeight” ile ayarlayın.
  • Docker'da ağır testler için bellek endişeleri varsa veya işleri daha verimli hale getirmeye yardımcı olmak istiyorsanız, "numTestsKeptInMemory" öğesini varsayılandan daha küçük bir sayıyla değiştirmeyi deneyebilir ve başarısız test için video yüklemeye odaklanmak için "videoUploadOnPasses" öğesini false olarak ayarlayabilirsiniz. sadece çalışır.

Başka bir notta, Cypress konfigürasyonunuzu değiştirdikten sonra, TypeScript ekleyebilir ve bu blog gönderisinde yaptığımız gibi Cypress testlerinizi daha iyi yazabilirsiniz. Bu özellikle otomatik tamamlama, tür uyarıları veya işlevleri çağırırken ve zincirlerken oluşan hatalar ( cy.task('someTaskPluginFunction) , Cypress.env('someEnvVariable') veya cy.customCommand() gibi) için faydalıdır.

Ayrıca, package.json dosyanızda farklı Cypress komut dosyaları çalıştırdığınızda, bir temel cypress.json dosyası kurmayı ve her test ortamı için ayrı bir Cypress yapılandırma dosyası yüklemeyi staging.json . Cypress belgeleri, bu yaklaşımda size yol göstermek için harika bir iş çıkarıyor.

Cypress klasörünüzü düzenleme

Cypress, kod tabanınızda Cypress'i ilk başlattığınızda, kılavuzlu bir yapıya sahip bir üst düzey cypress klasörü zaten kurar. Bu, spesifikasyon dosyalarınız için integration , JSON veri dosyaları için fixtures , cy.task(...) fonksiyonlarınız ve diğer konfigürasyonlarınız için plugins ve özel komutlarınız ve türleriniz için bir support klasörü gibi diğer klasörleri içerir.

Cypress klasörlerinizde, React bileşenlerinde veya genel olarak herhangi bir kodda düzenleme yaparken uyulması gereken iyi bir kural, birlikte değişmesi muhtemel şeyleri birlikte bir araya getirmektir. Bu durumda, tarayıcıdaki web uygulamalarıyla uğraştığımız için, iyi ölçeklenen bir yaklaşım, bir klasördeki şeyleri sayfaya veya kapsamlı özelliğe göre gruplandırmaktır.

"DomainAuthentication" (/settings/sender_auth/domains/**/*) ve "LinkBranding" gibi /settings/sender_auth yolunun altındaki tüm sayfa nesnelerini tutmak için "SenderAuthentication" gibi özellik adıyla bölümlenmiş ayrı bir pages klasörü oluşturduk. (/settings/sender_auth/links/**/*). plugins klasörümüzde, Gönderici Kimlik Doğrulaması için belirli bir özellik veya sayfa için tüm cy.task(...) eklenti dosyalarını aynı klasörde organize ederek aynı şeyi yaptık ve integration klasörümüz için aynı yaklaşımı izledik. spec dosyaları. Testlerimizi, kod tabanlarımızdan birindeki yüzlerce spesifikasyon dosyasına, sayfa nesnesine, eklentiye ve diğer dosyalara göre ölçeklendirdik ve gezinmesi kolay ve rahat.

İşte cypress klasörünü nasıl düzenlediğimizin bir özeti.

integration klasörünüzü (tüm özelliklerinizin bulunduğu yer) düzenlerken göz önünde bulundurmanız gereken bir diğer şey de, potansiyel olarak özellik dosyalarını testin önceliğine göre bölmektir. Örneğin, en yüksek öncelik ve değer testlerinin tümüne bir "P1" klasöründe ve ikinci öncelikli testlerin tümü bir "P2" klasöründe olabilir, böylece --spec seçeneğini aşağıdaki gibi ayarlayarak tüm "P1" testlerini kolayca çalıştırabilirsiniz --spec 'cypress/integration/P1/**/*' .

Spesifikasyonları yalnızca sayfaya veya --spec 'cypress/integration/SomePage/**/*' gibi özelliklere göre değil, aynı zamanda öncelik, ürün gibi diğer bazı kriterlere göre de kolayca gruplayabilmeniz için, sizin için çalışan bir özellik klasörü hiyerarşisi oluşturun. , veya çevre.

Eleman seçicilerini birleştirme

Sayfamızın React bileşenlerini geliştirirken, genellikle Jest ve Enzyme ile bir miktar birim ve entegrasyon testleri ekleriz. Özellik geliştirmenin sonuna doğru, her şeyin arka uçla çalıştığından emin olmak için başka bir Cypress E2E testi katmanı ekledik. Hem React bileşenlerimiz için birim testleri hem de Cypress E2E testlerimiz için sayfa nesneleri, etkileşimde bulunmak ve üzerinde iddiada bulunmak istediğimiz sayfadaki bileşenler/DOM ​​öğeleri için seçiciler gerektirir.

Bu sayfaları ve bileşenleri güncellediğimizde, birim test seçicilerinden Cypress sayfa nesnesine kadar birden çok yeri gerçek bileşen kodunun kendisine senkronize etmekten kaynaklanan sapma ve hatalar olma olasılığı vardır. Yalnızca stiller ile ilgili sınıf adlarına güvenseydik, kırılabilecek tüm yerleri güncellemeyi hatırlamak acı verici olurdu. Bunun yerine, hatırlamak istediğimiz sayfadaki belirli bileşenlere ve öğelere "data-hook", "data-testid" veya tutarlı olarak adlandırılan herhangi bir "data-*" özniteliği ekler ve bunun için test katmanlarımıza bir seçici yazarız.

Öğelerimizin çoğuna "veri kancası" öznitelikleri ekleyebiliriz, ancak yine de güncellemek ve diğer dosyalarda yeniden kullanmak için hepsini tek bir yerde gruplandırmanın bir yoluna ihtiyacımız vardı. React bileşenlerimize dağıtılacak ve dışa aktarılan nesnelerde daha fazla yeniden kullanım ve daha kolay bakım için birim testlerimizde ve sayfa nesne seçicilerimizde kullanılacak tüm bu "veri kancası" seçicilerini yönetmenin yazılı bir yolunu bulduk.

Her sayfanın en üst düzey klasörü için, öğe için okunabilir bir anahtar adına ve değer olarak öğe için gerçek dize CSS seçicisine sahip bir nesneyi yöneten ve dışa aktaran bir hooks.ts dosyası oluştururduk. Enzyme's wrapper.find(“[data-hook='selector']”) gibi çağrılarda bir öğe için CSS seçici formunu okumamız ve kullanmamız gerektiğinden bunlara “Okuma Seçicileri” diyeceğiz. cy.get(“[data-hook='selector']”) sayfa nesnelerimizde. Bu çağrılar daha sonra wrapper.find(Selectors.someElement) veya cy.get(Selectors.someElement) gibi daha temiz görünür.

Aşağıdaki kod parçacığı, bu okuma seçicilerini pratikte neden ve nasıl kullandığımızı daha ayrıntılı olarak ele almaktadır.

Benzer şekilde, öğe için okunabilir bir anahtar adına ve değer olarak { “data-hook”: “selector” } gibi bir nesneye sahip nesneleri de dışa aktarırız. Temel öğelere "data-hook" özniteliğini başarılı bir şekilde eklemek için bu nesneleri bir React bileşenine sahne olarak yazmamız veya yaymamız gerektiğinden bunlara "Yazma Seçicileri" diyeceğiz. Amaç böyle bir şey yapmak ve altındaki gerçek DOM öğesi (propların JSX öğelerine doğru bir şekilde aktarıldığı varsayılırsa) ayrıca “data-hook=” özniteliğine sahip olacaktır.

Aşağıdaki kod parçacığı, bu yazma seçicilerini pratikte neden ve nasıl kullandığımızı daha fazla kapsar.

Okuma seçicilerimizi birleştirmek için nesneler oluşturabilir ve daha az yeri güncellemek için yazma seçicilerimizi kullanabiliriz, ancak daha karmaşık sayfalarımızdan bazıları için çok sayıda seçici yazmak zorunda kalırsak ne olur? Bu, kendiniz oluşturmak için daha fazla hataya açık olabilir, bu nedenle, bu okuma seçicilerini kolayca oluşturmak için işlevler oluşturalım ve sonunda belirli bir sayfa için dışa aktarmak üzere seçicileri yazalım.

Okuma seçici oluşturucu işlevi için, bir girdi nesnesinin özellikleri arasında dolaşacağız ve her öğe adı için [data-hook=”selector”] CSS seçici dizesini oluşturacağız. Bir anahtarın karşılık gelen değeri null ise, giriş nesnesindeki öğe adının "data-hook" değeriyle aynı olacağını varsayacağız, örneğin { someElement: null } => { someElement: '[data-hook=”someElement”] } . Aksi takdirde, bir anahtarın karşılık gelen değeri boş değilse, { someElement: “newSelector” } => { someElement: '[data-hook=”newSelector”]' } gibi "data-hook" değerini geçersiz kılmayı seçebiliriz.

Yazma seçici oluşturucu işlevi için, bir girdi nesnesinin özellikleri arasında dolaşacağız ve her öğe adı için { “data-hook”: “selector” } nesneleri oluşturacağız. Bir anahtarın karşılık gelen değeri null ise, giriş nesnesindeki öğe adının "data-hook" değeriyle aynı olacağını varsayacağız, örneğin { someElement: null } => { someElement: { “data-hook”: “someElement” } } . Aksi takdirde, bir anahtarın karşılık gelen değeri null değilse, bu "data-hook" değerini geçersiz kılmayı seçebiliriz, örneğin { someElement: “newSelector” } => { someElement: { “data-hook”: “newSelector” }' } .

Bu iki üreteç işlevini kullanarak, bir sayfa için okuma seçicimizi ve yazma seçici nesnelerini oluşturur ve bunları birim testlerimizde ve Cypress sayfa nesnelerinde yeniden kullanılmak üzere dışa aktarırız. Başka bir bonus da, bu nesnelerin TypeScript dosyalarımızda yanlışlıkla Selectors.unknownElement veya WriteSelectors.unknownElement şekilde yazılmasıdır. Okuma seçicilerimizi dışa aktarmadan önce, üzerinde kontrolümüz olmayan üçüncü taraf bileşenler için fazladan öğe ve CSS seçici eşlemeleri eklemeye de izin veriyoruz. Bazı durumlarda, belirli öğelere bir "veri kancası" özniteliği ekleyemiyoruz, bu nedenle yine de diğer öznitelikler, kimlikler ve sınıflara göre seçmemiz ve aşağıda gösterildiği gibi okuma seçici nesnesine daha fazla özellik eklememiz gerekiyor.

Bu kalıp, bir sayfa için ve bir şeyleri güncellememiz gerektiğinde tüm seçicilerimizle düzenli kalmamıza yardımcı olur. Gelecekteki değişikliklere dokunmanız gereken dosya sayısını en aza indirmek için tüm bu seçicileri bir tür nesnede veya başka herhangi bir yolla yönetmenin yollarını araştırmanızı öneririz.

Konsolidasyon zaman aşımları

Birçok Cypress testi yazdıktan sonra fark ettiğimiz bir şey, belirli öğeler için seçicilerimizin zaman aşımına uğraması ve "defaultCommandTimeout" ve "responseTimeout" gibi cypress.json ayarlanan varsayılan zaman aşımı değerlerinden daha uzun sürmesiydi. Geri dönüp daha uzun zaman aşımlarına ihtiyaç duyan belirli sayfaları ayarlardık, ancak zamanla keyfi zaman aşımı değerlerinin sayısı arttı ve büyük ölçekli değişiklikler için bunu sürdürmek zorlaştı.

Sonuç olarak, zaman aşımlarımızı, seçicilerimiz için cy.get(...) veya cy.contains(...) . Bu varsayılan zaman aşımının ötesinde, Cypress komutlarımızda kullanmak üzere dosyalarımızda herhangi bir yere aktarabileceğimiz bir zaman aşımı nesnesi içinde “kısa”, “orta”, “uzun”, “xlong” ve “xxlong”a kadar ölçeklendiririz cy.get(“someElement”, { timeout: timeouts.short }) gibi cy.get(“someElement”, { timeout: timeouts.short }) veya cy.task('pluginName', {}, { timeout: timeouts.xlong }) . Zaman aşımlarımızı içe aktarılan nesnemizden bu değerlerle değiştirdikten sonra, belirli zaman aşımları için gereken süreyi büyütmek veya küçültmek için güncellenecek bir yerimiz olur.

Bununla nasıl kolayca başlayabileceğinize dair bir örnek aşağıda gösterilmiştir.

toparlamak

Cypress test takımlarınız büyüdükçe, Cypress testlerinizi en iyi şekilde nasıl ölçeklendireceğinizi ve sürdüreceğinizi belirlerken yaptığımız aynı sorunlardan bazılarıyla karşılaşabilirsiniz. Dosyalarınızı sayfaya, özelliğe veya diğer bazı gruplama kurallarına göre düzenlemeyi seçebilirsiniz, böylece daha fazla geliştirici kod tabanınıza katkıda bulundukça mevcut test dosyalarını nerede arayacağınızı ve yenilerini nereye ekleyeceğinizi her zaman bilirsiniz.

Kullanıcı arayüzü değiştikçe, "veri-" öznitelik seçicilerinizi her sayfanın üzerinde iddia etmek veya birim testleriniz ve Cypress içinde etkileşim kurmak istediğiniz anahtar öğelerine korumak için bir tür yazılan nesneyi (okuma ve yazma seçicileri gibi) kullanabilirsiniz. testler. Cypress komutlarınız için zaman aşımı değerleri gibi şeyler için çok fazla keyfi değer uygulamaya başladığınızı fark ederseniz, bu değerleri tek bir yerde güncelleyebilmeniz için ölçeklenmiş değerlerle dolu bir nesne kurmanın zamanı gelmiş olabilir.

Ön uç kullanıcı arabiriminizde, arka uç API'nizde ve Cypress testlerinizde işler değiştikçe, her zaman bu testleri daha kolay sürdürmenin yollarını düşünmelisiniz. Güncellemek ve hata yapmak için daha az yer ve şeylerin nereye yerleştirileceğine dair daha az belirsizlik, birçok geliştirici yeni sayfalar, özellikler ve (kaçınılmaz olarak) Cypress testleri eklediğinden büyük bir fark yaratır.

Cypress ile ilgili daha fazla gönderiyle ilgileniyor musunuz? Aşağıdaki makalelere bir göz atın:

  • E2E Testleri Yazarken Nelere Dikkat Edilmelidir?
  • Selvi Testleri Yazmaya 1.000 Ayak Genel Bakış
  • TypeScript Cypress Testlerinizdeki Her Şey
  • Cypress Testlerinde E-posta Akışlarıyla Başa Çıkmak
  • Docker, Buildkite ve CICD ile Cypress Testlerini Entegre Etme