Nedir Bu Cypress? E2E Test Üzerine

SHFT
5 min readMar 23, 2023

Sadece sizin için uygulamanızı kullanan ve sorunları raporlayan bir kullanıcınızın olduğunu düşünün. Evet bu mümkün, bu gün birlikte bunu nasıl yapabileceğimizi bakacağız.

Cypress Logo
Cypress Logo

E2E Test Nedir?

E2E yani End-to-End, Türkçe çevirisiyle uçtan uca test, uygulamanızı baştan sona belirlediğiniz senaryolar ile test edebileceğiniz bir test çeşidi. Diğer test çeşitlerinin aksine kod yapısı ve kodun çalışma yapısından habersiz olarak çalışan E2E testler, bir kullanıcı gibi uygulamayı kontrol eden test yapılarıdır.

Bu noktada test yaklaşımlarındaki Black Box ve White Box kavramlarına kısaca değinmeliyiz.

White Box Testing

Yazdığımız test business logic ile bağlantılı, kodun yapısı ve algoritma mekanizmasıyla birlikte çalışan testlerdir.

Black Box Testing

Uygulamanın kod yapısı, business logic veya herhangi bir algoritma ile bağlantısı olmadan bağımsız olarak dizayn edilen testlerdir. E2E testler bu kategoride yer alıyor.

Cypress

Cypress, Javascript ile web uygulamalarınız için Integration ve E2E testleri yazabileceğiniz bir araçtır. Kelime anlamı itibariyle Türkçesi Selvi (bknz. Hayat Ağacı) olan bir aracın E2E testler için kullanmamız da isimlendirme için manidar bir seçim olmuş 🙂

Kurulum

Kurulum için standalone bir uygulama olarak Cypress’i indirebileceğimiz gibi npm kullanarak da projemize entegre edebiliriz. Şimdilik hali hazırdaki web projemize entegre ettiğimiz bir kurulum üstünden devam edeceğiz.

Öncelikle proje gereksinimlerimize Cypress’i ekliyoruz:

npm install cypress - save-dev
//or
yarn add -D cypress

Bu noktada Cypress’i sadece geliştirme ortamı gereksinimine ekliyoruz.

Cypress’in canlı ortam içinde bulunmasına gerek yok (alternatif olarak bağımsız bir proje olarak da uygulamanızı test edecek bir yapı kurabilirsiniz).

Cypress’i çalıştırmak için:

npx cypress open
//or
yarn run cypress open

Akabinde Cypress arayüzü bizi karşılayacak.

Cypress Dashboard
Cypress dashboard

Buradan E2E testi seçerek devam ediyoruz. Konfigürasyonlardan sonra tarayıcı seçimini yaparak testleri çalıştırabiliriz.

Browser selection screen in Cypress dashboard
Browser selection in Cypress

İlk Testimiz

Örnek test için bir giriş sayfası simüle edeceğiz ve bu sayfa üzerinden testlerimizi çalıştıracağız.

Aşağıda giriş sayfası için DOM yapısı mevcut:

// pages/login.tsx

...
<form onSubmit={handleSubmit(handleSignIn)} data-cy="login-form">
{error && (
<div data-cy="error-box" className="...">
<p>{error}</p>
</div>
)}
<Form.Input type="text" data-cy="email" />
<Form.Input type="password" data-cy="password" />
<div className="...">
<Form.Checkbox data-cy="remember-checkbox" />
<Link href="/forgot-password">
<a className="...">Forgot Password</a>
</Link>
</div>
<Button type="submit" data-cy="submit" />
</form>
...

Tarayıcı da ise böyle görünüyor:

The login page form with error state
Login Page

Burada standart HTML element attributeleri dışında data-cy adında attribute görüyoruz, birazdan bunun ne olduğuna birlikte bakacağız.

Giriş sayfamız için yazdığımız test ise şöyle görünüyor:

// cypress/e2e/login.cy.ts
describe('Login Page', () => {
beforeEach(() => {
cy.visit('/');

cy.fixture('credentials.json').then(({ user }) => {
this.credentials = user;
});
});

it('The page should be login page', () => {
cy.get('#__next').should('exist').and('be.visible');

cy.url().should('include', '/login');
});

it('Login attempt with wrong credentials', () => {
cy.get('[data-cy=login-form]')
.should('exist')
.and('be.visible')
.within(() => {
cy.get('[data-cy=email]').type('lorem@ipsum.com').should('have.value', 'lorem@ipsum.com');
cy.get('[data-cy=password]').type('123456').should('have.value', '123456');
cy.get('[data-cy=submit]').click();
});

cy.get('[data-cy=login-form]').should('exist').and('be.visible');
cy.get('[data-cy=error-box]').should('exist').and('be.visible');
});

it('Login attempt with correct credentials', () => {
cy.intercept('POST', '/login').as('login');

cy.get('[data-cy=login-form]')
.should('exist')
.and('be.visible')
.within(() => {
const { email, password } = this.credentials;

cy.get('[data-cy=email]').type(email).should('have.value', email);
cy.get('[data-cy=password]').type(password).should('have.value', password);
cy.get('[data-cy=remember-checkbox]').click().should('be.checked');
cy.get('[data-cy=submit]').click();
});

cy.wait('@login').then((interception) => {
expect(interception.response.statusCode).to.equal(200);
});
});
});

Testi incelediğimizde;

describe” ile testimizin ilk tanımlamasını yapıyoruz. Burada metodolojik olarak testlerimizi senaryolara böldüğümüz ve elimizdeki senaryoda Giriş sayfasını test ettiğimiz için “title” kısmına “Login Page” yazıyoruz. Callback fonksiyonu içinde testimizin diğer tüm detayları yer alacak.

beforeEach” fonksiyonu her bir test adımından önce çalışacak şekilde dizayn edilmiş halde. Örnekte anasayfaya gitmesini söylüyoruz ve kullanıcı bilgilerini kaydettiğimiz JSON dosyasından alıyor.

it” fonksiyonu ile test adımlarımızın her birini tanımlıyoruz. Testimizi mümkün olan en küçük parçalara bölerek yapmak hata almamız durumunda tam olarak hangi adımda problem olduğunu daha kolay tespit edip çözmemizde yardımcı olacaktır.

Test adımlarımız şu şekilde:

  • beforeEach” ile anasayfaya gideceğiz.(test ettiğimiz uygulamanın anasayfasının kullanıcı girişine bağımlı olduğunu varsayıyoruz. Bir dashboard veya internal app olarak düşünebiliriz).
  • İlk adımda tarayıcıda açılan sayfanın “/login” olmasını bekliyoruz. Bu sayede kullanıcılar giriş yaptıktan sonra anasayfaya ulaşabilecekler.
  • İkinci adımda yanlış kullanıcı bilgileri ile giriş yapmayı deniyoruz. Neden ilk hatalı girişi deniyoruz? Çünkü kullanıcının yanlış bilgi girdiğinde giriş yapamadığından emin olmamız gerekiyor. Ayrıca yanlış bilgiler ile giriş yapılmaya çalışıldığında kullanıcıya geri bildirim verildiğinden de emin oluyoruz.
  • Üçüncü ve son adımımızda doğru kullanıcı bilgileri ile giriş yapmayı deniyoruz. Kullanıcı sorunsuz şekilde giriş yapıp uygulamaya ulaşıp ulaşamadığını kontrol ediyoruz.

Fixtures

beforeEach” adımında kullandığımız “cy.fixtures” fonksiyonu var, bu fonksiyon E2E testler içerisinde kullanacağımız değişkenleri almak için Cypress kütüphanesi içinde bulunuyor.

Peki bu değerleri nereden alıyoruz? Cypress’i ilk çalıştırdığınızda projenizin içinde “cypress” adında bir klasör oluşturuyor. Bu klasörün içinde test sonuçlarının video kaydı, ekran görüntüleri, konfigürasyon dosyaları bulunduğu gibi “fixtures” adında klasör bulunuyor. Tam olarak bu klasörün içinde oluşturacağımız JSON dosyasında değişkenlerimizi tutuyoruz.

// cypress/fixtures/credentials.json

{
"user": {
"email": "test.e2e@shft.co",
"password": "123456"
}
}

Network İstekleri

Testte giriş aksiyonunu kontrol etmek için network isteklerini dinlememiz gerekiyor. Bunun için Cypress içinde bulunan “cy.intercept” fonksiyonunu kullanıyoruz.

Burada fonksiyona istek atılmasını beklediğimiz istek methodu ve istek URL’ini veriyoruz. Ayrıca “as” fonksiyonu ile bu isteği test içinde isimlendirmiş oluyoruz.

Akabinde verdiğimiz bu isimlendirmeyi “cy.wait” içinde belirterek isteği dinliyoruz. İstekten dönen cevaba göre testimizin senaryosuna uygun response aldığımızdan emin oluyoruz. Burada network isteğini dinlemek için kullandığımız fonksiyona direkt ms değer vererek sadece beklemesi için de kullanabiliriz.

Best Practices

Bu noktada artık HTML element attribute’larına değinebiliriz. Cypress’de bir elemente ulaşmak için birden fazla method var. Elementin id, class gibi attribute’ları üstünden ulaşabiliriz. Ancak bu attribute’lar geliştirme ortamı ile direkt bağlantılı olduğu için bunların değişmesi halinde testten hata alacağız. Bunun yerine “data-cy” attribute’una string bir değer vererek development ortamından bağımsız elementimizi bir nevi etiketlemiş oluyoruz ve bu etiket sadece Cypress testlerimiz ile ilintili olmuş oluyor.

Bunun gibi bir çok pratik yol mevcut, Cypress’in sitesinden diğerlerine de göz atabilirsiniz.

(https://docs.cypress.io/guides/references/best-practices)

Cypress vs Playwright

Kısaca bu iki test aracını kıyaslamak gerekirse:

Kullanım kolaylığı: Cypress, kullanım kolaylığı ve hızlı kurulumuyla bilinir. Syntax’ın anlaşılması ve yazılması kolaydır, bu da onu yeni başlayanlar için harika bir seçim yapar. Playwright ise daha fazla kurulum ve konfigürasyon gerektirir.

Built-in assertion: Cypress, ek paketlere ihtiyaç duymadan test yazmayı ve çalıştırmayı kolaylaştıran built-in assertions(Mocha) birlikte gelir. Playwright, kurulum sürecine fazladan bir adım ekleyen ayrı paket gerektirir.

Daha kolay debug: Cypress, video kaydı ve etkileşimli hata ayıklama dahil olmak üzere debug imkanı sunar. Bu, test sırasında ortaya çıkan sorunları tanımlamayı ve düzeltmeyi kolaylaştırır.

Daha hızlı testler: Test yürütme söz konusu olduğunda Cypress, Playwright’tan daha hızlıdır. Bunun nedeni, Cypress’in test edilen uygulamayla aynı thread içinde çalıştırmasıdır, bu da onu daha verimli kılar. Öte yandan Playwright, testin yürütülmesini yavaşlatabilen ayrı threadler halinde çalışır.

Topluluk: Cypress, Playwright’tan daha büyük ve daha aktif bir topluluğa sahiptir. Bu, Cypress ile ilgili sorunları öğrenmek ve gidermek için daha fazla kaynak olduğu anlamına gelir.

Özet

Genel hatlarıyla SHFT’de E2E testleri böyle yazıyoruz. Cypress’i Frontend projelerimizde uzun süre kullanacağız gibi görünüyor. Umarım örneklerle birlikte anlamanıza yardımcı olabilmişizdir. Bir sonraki yazıda görüşmek üzere, soru ve önerilerini sosyal medya hesaplarımızdan veya direkt bize email atarak ulaştırabilirsiniz.

Batur Akkurt | Tech Lead

--

--