Meslekte kullanılan araçlar

Otomatik testler, temelde yalnızca bir şeyler yanlış olduğunda hataya neden olan veya hataya neden olan kodlardır. Çoğu kitaplık veya test çerçevesi, testlerin daha kolay yazılmasını sağlayan çeşitli temel öğeler sağlar.

Bir önceki bölümde belirtildiği gibi, bu temel öğeler neredeyse her zaman bağımsız testler tanımlama (test durumları olarak adlandırılır) ve iddialar sunma yöntemini içerir. Onaylamalar, bir sonucu kontrol etme ve bir şey yanlışsa hata verme işlemlerini birleştirmenin bir yoludur ve teste ilişkin tüm temel öğelerin temel öğesi olarak kabul edilebilir.

Bu sayfa, bu temel öğelere genel bir yaklaşım sağlar. Seçtiğiniz çerçevede muhtemelen buna benzer bir şey var ama bu tam bir referans değil.

Örneğin:

import { fibonacci, catalan } from '../src/math.js';
import { assert, test, suite } from 'a-made-up-testing-library';

suite('math tests', () => {
  test('fibonacci function', () => {
    // check expected fibonacci numbers against our known actual values
    // with an explanation if the values don't match
    assert.equal(fibonacci(0), 0, 'Invalid 0th fibonacci result');
    assert.equal(fibonacci(13), 233, 'Invalid 13th fibonacci result');
  });
  test('relationship between sequences', () => {
    // catalan numbers are greater than fibonacci numbers (but not equal)
    assert.isAbove(catalan(4), fibonacci(4));
  });
  test('bugfix: check bug #4141', () => {
    assert.isFinite(fibonacci(0)); // fibonacci(0) was returning NaN
  })
});

Bu örnek, "matematik testleri" adında bir test grubu (bazen paket olarak adlandırılır) oluşturur ve her biri bazı onaylar çalıştıran üç bağımsız test durumu tanımlar. Bu test durumları genellikle ayrı ayrı ele alınabilir veya çalıştırılabilir (örneğin, test çalıştırıcınızdaki bir filtre işaretiyle).

Temel öğeler olarak onaylama yardımcıları

Vitest de dahil olmak üzere çoğu test çerçevesi, dönüş değerlerini veya diğer durumları bazı expectation göre hızlı bir şekilde kontrol etmenize olanak tanıyan bir assert nesnesinde onaylama yardımcıları koleksiyonu içerir. Bu beklenti genellikle "iyi bilinen" değerlerdir. Önceki örnekte, 13. Fibonacci sayısının 233 olması gerektiğini bildiğimiz için bunu doğrudan assert.equal kullanarak doğrulayabiliriz.

Bir değerin belirli bir biçimde olması, başka bir değerden büyük olması veya başka bir özelliğe sahip olması yönünde de beklentileriniz olabilir. Bu kursta, olası onaylama yardımcılarının tamamı ele almayacaktır ancak test çerçeveleri her zaman en azından aşağıdaki temel kontrolleri sağlar:

  • Çoğunlukla "tamam" kontrolü olarak tanımlanan 'truthy' kontrolü, bir koşulun doğru olup olmadığını kontrol ederek bir koşulun başarılı veya doğru olup olmadığını kontrol eden bir if yazabilmenizi sağlar. Bu genellikle assert(...) veya assert.ok(...) olarak sağlanır ve tek bir değer ile isteğe bağlı bir yorum alır.

  • Matematik testi örneğindeki gibi bir nesnenin dönüş değerinin veya durumunun bilinen iyi bir değere eşit olmasını beklediğiniz bir eşitlik kontrolü. Bunlar, temel eşitlik (sayılar ve dizeler için gibi) veya referanslı eşitlik (bunlar aynı nesnedir) içindir. Temelde, bunlar yalnızca == veya === karşılaştırması içeren "doğru" bir kontroldür.

    • JavaScript, serbest (==) ve katı (===) eşitliği birbirinden ayırır. Çoğu test kitaplığı size sırasıyla assert.equal ve assert.strictEqual yöntemlerini sunar.
  • Derin eşitlik kontrolleri; nesnelerin, dizilerin ve diğer karmaşık veri türlerinin içeriklerinin kontrol edilmesini ve bunları karşılaştırmak için nesneleri aktarmak için dahili mantığın kontrol edilmesini de içerecek şekilde genişletilir. JavaScript'in iki nesnenin veya dizinin içeriğini karşılaştırmak için yerleşik bir yöntemi olmadığından bunlar önemlidir. Örneğin, [1,2,3] == [1,2,3] her zaman "yanlış"tır. Test çerçeveleri genellikle deepEqual veya deepStrictEqual yardımcılarını içerir.

İki değeri karşılaştıran onay yardımcıları (yalnızca "doğruluk" kontrolü yerine) genellikle iki veya üç bağımsız değişken alır:

  • Test edilen koddan oluşturulan veya doğrulanacak durumu açıklayan gerçek değer.
  • Beklenen değer; genellikle sabit kodludur (ör. değişmez sayı veya dize).
  • Bu satır başarısız olursa nelerin beklendiğini veya nelerin başarısız olabileceğini açıklayan isteğe bağlı bir yorum.

Ayrıca çeşitli kontroller oluşturmak için iddiaları birleştirmek oldukça yaygın bir uygulamadır. Çünkü sisteminizin durumunu tek başına doğru şekilde doğrulayabilmeniz nadir görülen bir durumdur. Örneğin:

  test('JWT parse', () => {
    const json = decodeJwt('eyJieSI6InNhbXRob3Ii…');

    assert.ok(json.payload.admin, 'user should be admin');
    assert.deepEqual(json.payload.groups, ['role:Admin', 'role:Submitter']);
    assert.equal(json.header.alg, 'RS265')
    assert.isAbove(json.payload.exp, +new Date(), 'expiry must be in future')
  });

Vitest, iddia yardımcılarını sağlamak için dahili olarak Chai onaylama kitaplığı'nı kullanır. Kodunuza uygun onay ve yardımcıların neler olduğunu görmek için bu kitaplığın referansını incelemek faydalı olabilir.

Akıcı ve BDD onayları

Bazı geliştiriciler, davranışa dayalı geliştirme (BDD) veya Akıcı tarzında iddialar olarak adlandırılan bir onaylama stilini tercih ederler. Beklentileri kontrol etmenin giriş noktası expect() adlı bir yöntem olduğundan bunlara "beklenti" yardımcıları da denir.

Yardımcıların assert.ok veya assert.strictDeepEquals gibi basit yöntem çağrıları olarak yazılan onaylarla aynı şekilde davranmasını bekleyebilirsiniz ancak bazı geliştiriciler bunları daha kolay okunur. BDD onayı aşağıdaki gibi olabilir:

// A failure here would generate "Expect result to be an array that does include 42"
const result = await possibleMeaningsOfLife();
expect(result).to.be.an('array').that.does.include(42);

// or a simpler form
expect(result).toBe('array').toContainEqual(42);

// the same in assert might be
assert.typeOf(result, 'array', 'Expected the result to be an array');
assert.include(result, 42, 'Expected the result to include 42');

Bu onay türleri, expect tarafından döndürülen nesnenin sürekli olarak başka yöntem çağrılarıyla birlikte zincirlendiği yöntem zincirleme adı verilen bir teknik nedeniyle çalışır. Önceki örnekte verilen to.be ve that.does dahil olmak üzere çağrının bazı bölümleri hiçbir işleve sahip değildir. Bunlar yalnızca çağrının okunmasını kolaylaştırmak ve test başarısız olursa otomatik bir yorum oluşturmak için dahil edilmiştir. (Zincirleme bağlantının hatayı net bir şekilde açıklaması gerektiği için expect normalde isteğe bağlı yorumları desteklemez.)

Birçok test çerçevesi hem Fluent/BDD'yi hem de normal onaylamaları destekler. Örneğin, Vitest, Chai'nin her iki yaklaşımını da dışa aktarır ve BDD ile ilgili olarak biraz daha kısa ve öz bir yaklaşıma sahiptir. Öte yandan Jest, varsayılan olarak yalnızca bir beklenti yöntemi içerir.

Testleri dosyalar arasında gruplandırma

Test yazarken zaten örtülü gruplamalar sunma eğilimindeyiz. Tüm testlerin tek bir dosyada olması yerine, testlerin birden fazla dosyaya yazılması yaygın bir durumdur. Aslında, test çalıştırıcıları yalnızca önceden tanımlanmış bir filtre veya normal ifade nedeniyle bir dosyanın test için olduğunu bilir. Örneğin, vitest, projenizdeki ".test.jsx" ya da ".spec.ts" (".test" ve ".spec" gibi bir uzantıyla biten tüm dosyaları ve geçerli bazı uzantıları içerir.)

Bileşen testleri, aşağıdaki dizin yapısında olduğu gibi, test edilen bileşenin bir eş dosyasında bulunur:

UserList.tsx ve UserList.test.tsx dahil olmak üzere, bir dizindeki dosyaların listesi.
Bir bileşen dosyası ve ilgili test dosyası.

Benzer şekilde, birim testleri test edilen kodun yanına yerleştirilir. Uçtan uca testlerin her biri kendi dosyasında olabilir. Entegrasyon testleri de kendi benzersiz klasörlerine yerleştirilebilir. Bu yapılar, karmaşık test durumları büyüdükçe ve yalnızca test için gereken destek kitaplıkları gibi test dışı destek dosyalarına ihtiyaç duyulduğunda faydalı olabilir.

Testleri dosyalar içinde gruplandırma

Önceki örneklerde kullanıldığı gibi, test() ile ayarladığınız testleri gruplandıran bir suite() çağrısının içine test yerleştirmek yaygın bir uygulamadır. Paketler genellikle kendi kendine test yapmaz ancak iletilen yöntemi çağırarak ilgili testleri veya hedefleri gruplandırarak yapı sağlanmasına yardımcı olur. test() için geçilen yöntem, testin kendi eylemlerini açıklar.

İddialarda olduğu gibi Aksan/BDD testlerinde de gruplama testleriyle oldukça standart bir eşdeğer vardır. Bazı tipik örnekler aşağıdaki kodda karşılaştırılmıştır:

// traditional/TDD
suite('math tests', () => {
  test('handle zero values', () => {
    assert.equal(fibonacci(0), 0);
  });
});

// Fluent/BDD
describe('math tests', () => {
  it('should handle zero values', () => {
    expect(fibonacci(0)).toBe(0);
  });
})

Onay yazmak için expect ve assert kullanılması arasındaki büyük farkın aksine suite ve describe ile test ile it benzer şekilde davranır.

Bazı araçların ise paket ve test düzenleme konusunda çok farklı yaklaşımları vardır. Örneğin, Node.js'nin yerleşik test çalıştırıcısı, dolaylı olarak bir test hiyerarşisi oluşturmak için test() öğesine yapılan iç içe yerleştirme çağrılarını destekler. Ancak Vitest, yalnızca suite() kullanarak bu tür iç içe yerleştirmeye izin verir ve başka bir test() içinde tanımlanmış bir test() çalıştırmaz.

Onaylarda olduğu gibi, teknoloji yığınınızın sağladığı gruplama yöntemlerinin tam kombinasyonunun o kadar önemli olmadığını unutmayın. Bu kursta bunları özet olarak ele alacağız ama seçtiğiniz araçlara nasıl uyguladıklarını öğrenmeniz gerekir.

Yaşam döngüsü yöntemleri

Testlerinizi bir dosya içinde en üst düzeyde bile olsa gruplandırmanın bir nedeni, her test veya bir grup test için bir kez çalıştırılacak kurulum ve ayırma yöntemleri sağlamaktır. Çoğu çerçeve dört yöntem sunar:

Her "test()" veya "it()" için Süit için bir kez
Test çalıştırılmadan önce "beforeEvery()" "beforeAll()"
Test çalıştırmalarından sonra "afterEvery()" "afterAll()"

Örneğin, her testten önce bir sanal kullanıcı veritabanını önceden doldurmak ve daha sonra temizlemek isteyebilirsiniz:

suite('user test', () => {
  beforeEach(() => {
    insertFakeUser('bob@example.com', 'hunter2');
  });
  afterEach(() => {
    clearAllUsers();
  });

  test('bob can login', async () => { … });
  test('alice can message bob', async () => { … });
});

Bu, testlerinizi basitleştirmek için yararlı olabilir. Her testte çoğaltmak yerine ortak kurulum ve ayırma kodunu paylaşabilirsiniz. Buna ek olarak, kurulum ve ayırma kodunun kendisi hata veriyorsa testlerin kendiliğinden başarısız olduğu yapısal sorunlar olduğu anlamına gelebilir.

Genel tavsiye

Bu temel unsurları düşünürken unutmamanız gereken birkaç ipucunu burada bulabilirsiniz.

Temel öğeler yol göstericidir

Buradaki ve sonraki birkaç sayfada yer alan araçların ve temel öğelerin, Vitest, Jest, Mocha, Web Test Runner veya diğer özel çerçevelerle tam olarak eşleşmeyeceğini unutmayın. Genel bir kılavuz olarak Vitest'ten yararlanmış olsak da, bunları seçtiğiniz çerçeveyle eşleştirdiğinizden emin olun.

Onayları gerektiği gibi karıştırın ve eşleştirin

Testler temelde hata veren kodlardır. Her koşucu farklı test durumlarını tanımlamak için bir temel (muhtemelen test()) sağlar.

Ancak bu çalıştırıcı aynı zamanda assert(), expect() ve onaylama yardımcıları da sağlıyorsa bu bölümün daha çok kolaylıkla ilgili olduğunu ve gerekirse bu bölümü atlayabilirsiniz. Diğer onay kitaplıkları veya eski tarz bir if ifadesi de dahil olmak üzere hataya neden olabilecek tüm kodları çalıştırabilirsiniz.

IDE kurulumu cankurtarabilir

VSCode gibi IDE'nizin otomatik tamamlamaya ve seçtiğiniz test araçlarındaki belgelere erişebildiğinden emin olmak daha üretken olmanızı sağlayabilir. Örneğin, assert üzerinde Chai onay kitaplığında 100'den fazla yöntem bulunmaktadır ve doğru doğrulama kitaplığının satır içinde görünmesi kullanışlı olabilir.

Bu, özellikle global ad alanını test yöntemleriyle dolduran bazı test çerçeveleri için önemli olabilir. Bu küçük bir farktır ancak genel ad alanına otomatik olarak eklenmişlerse test kitaplıklarını içe aktarmadan kullanmak genellikle mümkündür:

// some.test.js
test('using test as a global', () => { … });

Otomatik olarak desteklenseler bile yardımcıları içe aktarmanızı öneririz. Böylece IDE'niz bu yöntemleri kolayca arayabilir. (Bazı kod tabanlarında sihirli bir global React olduğu, ancak bazılarının böyle olmadığı ve React kullanılarak tüm dosyalara aktarılmasını gerektirdiği için React'ı oluştururken bu sorunu yaşamış olabilirsiniz.)

// some.test.js
import { test } from 'vitest';
test('using test as an import', () => { … });