Gerçekte Entegrasyon Testleri Yapmaya Yönelik Bir Node.js Kılavuzu
Yayınlanan: 2022-03-11Entegrasyon testleri korkulması gereken bir şey değildir. Bunlar, uygulamanızın tamamen test edilmesinin önemli bir parçasıdır.
Testten bahsederken, genellikle tek başına küçük bir kod parçasını test ettiğimiz birim testleri düşünürüz. Ancak, uygulamanız bu küçük kod yığınından daha büyüktür ve uygulamanızın neredeyse hiçbir parçası tek başına çalışmaz. Entegrasyon testlerinin önemini kanıtladığı yer burasıdır. Entegrasyon testleri, birim testlerinin yetersiz kaldığı yerden başlar ve birim testleri ile uçtan uca testler arasındaki boşluğu doldurur.
Bu makalede, API tabanlı uygulamalarda örneklerle okunabilir ve şekillendirilebilir entegrasyon testleri yazmayı öğreneceksiniz.
Bu makaledeki tüm kod örnekleri için JavaScript/Node.js kullanacak olsak da, tartışılan fikirlerin çoğu herhangi bir platformdaki entegrasyon testlerine kolayca uyarlanabilir.
Birim Testleri ve Entegrasyon Testleri: Her İkisine de İhtiyacınız Var
Birim testleri, belirli bir kod birimine odaklanır. Genellikle bu, belirli bir yöntem veya daha büyük bir bileşenin işlevidir.
Bu testler, tüm dış bağımlılıkların tipik olarak inatçı veya alay konusu olduğu izole bir şekilde yapılır.
Başka bir deyişle, bağımlılıklar önceden programlanmış davranışlarla değiştirilir ve testin sonucunun yalnızca test edilen birimin doğruluğu tarafından belirlenmesini sağlar.
Birim testleri hakkında daha fazla bilgiyi buradan edinebilirsiniz.
Birim testleri, iyi tasarımla yüksek kaliteli kodu korumak için kullanılır. Ayrıca köşe kasalarını kolayca kapatmamızı sağlarlar.
Ancak dezavantajı, birim testlerinin bileşenler arasındaki etkileşimi kapsayamamasıdır. Entegrasyon testlerinin yararlı olduğu yer burasıdır.
Entegrasyon Testleri
Birim testleri, en küçük kod birimlerini ayrı ayrı test ederek tanımlanırsa, entegrasyon testleri tam tersidir.
Entegrasyon testleri, etkileşim halindeki birden fazla, daha büyük birimi (bileşeni) test etmek için kullanılır ve bazen birden fazla sistemi kapsayabilir.
Entegrasyon testlerinin amacı, aşağıdakiler gibi çeşitli bileşenler arasındaki bağlantı ve bağımlılıklardaki hataları bulmaktır:
- Geçersiz veya yanlış sıralanmış argümanları iletme
- Bozuk veritabanı şeması
- Geçersiz önbellek entegrasyonu
- İş mantığındaki kusurlar veya veri akışındaki hatalar (çünkü testler artık daha geniş bir bakış açısıyla yapılıyor).
Test ettiğimiz bileşenlerin herhangi bir karmaşık mantığı yoksa (örneğin minimum döngüsel karmaşıklığa sahip bileşenler), entegrasyon testleri birim testlerinden çok daha önemli olacaktır.
Bu durumda, öncelikle iyi bir kod tasarımını uygulamak için birim testleri kullanılacaktır.
Birim testleri, işlevlerin düzgün bir şekilde yazıldığından emin olmaya yardımcı olurken, entegrasyon testleri, sistemin bir bütün olarak düzgün çalışmasını sağlamaya yardımcı olur. Dolayısıyla hem birim testleri hem de entegrasyon testleri kendi tamamlayıcı amaçlarına hizmet eder ve her ikisi de kapsamlı bir test yaklaşımı için gereklidir.
Birim testleri ve entegrasyon testleri aynı madalyonun iki yüzü gibidir. Madeni para ikisi olmadan geçerli değildir.
Bu nedenle, hem entegrasyonu hem de birim testlerini tamamlayana kadar test tamamlanmaz.
Entegrasyon Testleri için Paketi Ayarlayın
Birim testleri için bir test takımı kurmak oldukça basit olsa da, entegrasyon testleri için bir test takımı kurmak çoğu zaman daha zordur.
Örneğin, entegrasyon testlerindeki bileşenlerin veritabanları, dosya sistemleri, e-posta sağlayıcıları, harici ödeme hizmetleri vb. gibi proje dışında olan bağımlılıkları olabilir.
Bazen entegrasyon testlerinin bu harici hizmetleri ve bileşenleri kullanması gerekir ve bazen bunlar saplanabilir.
Gerektiğinde, çeşitli zorluklara yol açabilir.
- Kırılgan test yürütme: Harici hizmetler kullanılamayabilir, geçersiz bir yanıt verebilir veya geçersiz bir durumda olabilir. Bazı durumlarda, bu yanlış bir pozitif ile sonuçlanabilir, diğer zamanlarda yanlış bir negatif ile sonuçlanabilir.
- Yavaş yürütme: Hazırlanma ve harici hizmetlere bağlanma yavaş olabilir. Genellikle testler, CI'nin bir parçası olarak harici bir sunucuda çalıştırılır.
- Karmaşık test kurulumu: Harici hizmetlerin test için istenen durumda olması gerekir. Örneğin, veritabanı gerekli test verileri vb. ile önceden yüklenmiş olmalıdır.
Entegrasyon Testleri Yazarken İzlenecek Yönergeler
Entegrasyon testlerinin birim testleri gibi katı kuralları yoktur. Buna rağmen, entegrasyon testleri yazarken izlenmesi gereken bazı genel yönergeler vardır.
Tekrarlanabilir Testler
Test sırası veya bağımlılıklar, test sonucunu değiştirmemelidir. Aynı testi birden çok kez çalıştırmak her zaman aynı sonucu vermelidir. Test, üçüncü taraf hizmetlere bağlanmak için İnternet kullanıyorsa bunu başarmak zor olabilir. Ancak, bu sorun saplama ve alay etme yoluyla çözülebilir.
Üzerinde daha fazla kontrole sahip olduğunuz dış bağımlılıklar için, tümleştirme testinden önce ve sonra adımların ayarlanması, testin her zaman aynı durumdan başlayarak çalıştırıldığından emin olmanıza yardımcı olur.
İlgili Eylemleri Test Etme
Tüm olası durumları test etmek için birim testleri çok daha iyi bir seçenektir.
Entegrasyon testleri daha çok modüller arasındaki bağlantıya yöneliktir, bu nedenle mutlu senaryoları test etmek genellikle gidilecek yoldur çünkü modüller arasındaki önemli bağlantıları kapsayacaktır.
Anlaşılır Test ve İddia
Testin hızlı bir görünümü, okuyucuya neyin test edildiğini, ortamın nasıl kurulduğunu, neyin stubbed olduğunu, testin ne zaman yürütüldüğünü ve neyin ileri sürüldüğünü bildirmelidir. İddialar basit olmalı ve daha iyi karşılaştırma ve günlüğe kaydetme için yardımcılardan yararlanmalıdır.
Kolay Test Kurulumu
Testi başlangıç durumuna getirmek mümkün olduğunca basit ve anlaşılır olmalıdır.
Üçüncü Taraf Kodunu Test Etmekten Kaçının
Testlerde üçüncü taraf hizmetleri kullanılabilse de, bunları test etmeye gerek yoktur. Ve onlara güvenmiyorsanız, muhtemelen onları kullanmamalısınız.
Üretim Kodunu Test Kodundan Arındırın
Üretim kodu temiz ve anlaşılır olmalıdır. Test kodunun üretim koduyla karıştırılması, birbirine bağlanamayan iki alanın birbirine bağlanmasına neden olur.
İlgili Günlüğe Kaydetme
Başarısız testler, iyi bir günlük kaydı olmadan çok değerli değildir.
Testler geçtiğinde, ekstra günlük kaydı gerekmez. Ancak başarısız olduklarında, kapsamlı günlük kaydı hayati önem taşır.
Günlüğe kaydetme, tüm veritabanı sorgularını, API isteklerini ve yanıtlarını ve ayrıca iddia edilenlerin tam bir karşılaştırmasını içermelidir. Bu, hata ayıklamayı önemli ölçüde kolaylaştırabilir.
İyi Testler Temiz ve Anlaşılır Görünür
Buradaki yönergeleri izleyen basit bir test şöyle görünebilir:
const co = require('co'); const test = require('blue-tape'); const factory = require('factory'); const superTest = require('../utils/super_test'); const testEnvironment = require('../utils/test_environment_preparer'); const path = '/v1/admin/recipes'; test(`API GET ${path}`, co.wrap(function* (t) { yield testEnvironment.prepare(); const recipe1 = yield factory.create('recipe'); const recipe2 = yield factory.create('recipe'); const serverResponse = yield superTest.get(path); t.deepEqual(serverResponse.body, [recipe1, recipe2]); }));
Yukarıdaki kod, yanıt olarak bir dizi kaydedilmiş tarif döndürmesini bekleyen bir API'yi ( GET /v1/admin/recipes
) test ediyor.
Testin, ne kadar basit olursa olsun, birçok yardımcı programa dayandığını görebilirsiniz. Bu, herhangi bir iyi entegrasyon test paketi için yaygındır.
Yardımcı bileşenler, anlaşılır entegrasyon testleri yazmayı kolaylaştırır.
Entegrasyon testi için hangi bileşenlerin gerekli olduğunu gözden geçirelim.
Yardımcı Bileşenler
Kapsamlı bir test paketinde, akış denetimi, test çerçevesi, veritabanı işleyicisi ve arka uç API'lerine bağlanmanın bir yolu dahil olmak üzere birkaç temel bileşen bulunur.
Akış kontrolü
JavaScript testindeki en büyük zorluklardan biri asenkron akıştır.
Geri aramalar kodda hasara yol açabilir ve vaatler yeterli değildir. Akış yardımcılarının işe yaradığı yer burasıdır.
Async/await'in tam olarak desteklenmesini beklerken, benzer davranışa sahip kitaplıklar kullanılabilir. Amaç, zaman uyumsuz akışa sahip olma olasılığı ile okunabilir, anlamlı ve sağlam kod yazmaktır.
Co, kodun bloke olmamasını sağlarken güzel bir şekilde yazılmasını sağlar. Bu, bir ortak oluşturucu işlevi tanımlayarak ve ardından sonuçlar vererek yapılır.
Başka bir çözüm Bluebird kullanmaktır. Bluebird dizileri, hataları, zamanı vb. işleme gibi çok kullanışlı özelliklere sahip bir söz kitaplığıdır.
Co ve Bluebird coroutine, ES7'deki async/await'e benzer şekilde davranır (devam etmeden önce çözümü bekler), tek fark, her zaman bir söz vermesidir, bu da hataları işlemek için yararlıdır.
Test Çerçevesi
Bir test çerçevesi seçmek sadece kişisel tercihinize bağlıdır. Benim tercihim kullanımı kolay, yan etkisi olmayan, çıktısı kolay okunabilen ve aktarılabilen bir framework.

JavaScript'te çok çeşitli test çerçeveleri vardır. Örneklerimizde Bant kullanıyoruz. Tape, bence, sadece bu gereklilikleri yerine getirmekle kalmıyor, aynı zamanda Mocha veya Jasmin gibi diğer test çerçevelerinden daha temiz ve basit.
Bant, Her Şeyi Test Etme Protokolünü (TAP) temel alır.
TAP'ın çoğu programlama dili için varyasyonları vardır.
Tape, testleri girdi olarak alır, çalıştırır ve ardından sonuçları bir TAP olarak verir. TAP sonucu daha sonra test muhabirine iletilebilir veya ham formatta konsola çıkarılabilir. Teyp komut satırından çalıştırılır.
Teyp, tüm test paketini çalıştırmadan önce yüklenecek bir modül tanımlamak, küçük ve basit bir onaylama kitaplığı sağlamak ve bir testte çağrılması gereken onaylama sayısını tanımlamak gibi bazı güzel özelliklere sahiptir. Önceden yüklemek için bir modül kullanmak, bir test ortamı hazırlamayı basitleştirebilir ve gereksiz tüm kodları kaldırabilir.
Fabrika Kitaplığı
Bir fabrika kitaplığı, statik fikstür dosyalarınızı bir test için veri oluşturmak için çok daha esnek bir yolla değiştirmenize olanak tanır. Böyle bir kitaplık, dağınık, karmaşık kodlar yazmadan modeller tanımlamanıza ve bu modeller için varlıklar oluşturmanıza olanak tanır.
JavaScript bunun için fabrika_kızına sahiptir - orijinal olarak Ruby on Rails için geliştirilmiş, benzer bir ada sahip bir mücevherden esinlenilmiş bir kitaplık.
const factory = require('factory-girl').factory; const User = require('../models/user'); factory.define('user', User, { username: 'Bob', number_of_recipes: 50 }); const user = factory.build('user');
Başlamak için, fabrika_kızında yeni bir model tanımlanmalıdır.
Bir ad, projenizden bir model ve yeni bir örneğin oluşturulduğu bir nesne ile belirtilir.
Alternatif olarak, yeni bir örneğin oluşturulduğu nesneyi tanımlamak yerine, bir nesne veya bir söz döndürecek bir işlev sağlanabilir.
Bir modelin yeni bir örneğini oluştururken şunları yapabiliriz:
- Yeni oluşturulan örnekteki herhangi bir değeri geçersiz kıl
- Yapı işlevi seçeneğine ek değerler iletin
Bir örnek görelim.
const factory = require('factory-girl').factory; const User = require('../models/user'); factory.define('user', User, (buildOptions) => { return { name: 'Mike', surname: 'Dow', email: buildOptions.email || '[email protected]' } }); const user1 = factory.build('user'); // {"name": "Mike", "surname": "Dow", "email": "[email protected]"} const user2 = factory.build('user', {name: 'John'}, {email: '[email protected]'}); // {"name": "John", "surname": "Dow", "email": "[email protected]"}
API'lere bağlanma
Tam gelişmiş bir HTTP sunucusunu başlatmak ve gerçek bir HTTP isteğinde bulunmak, yalnızca birkaç saniye sonra onu yıkmak için – özellikle birden fazla test yaparken – tamamen verimsizdir ve entegrasyon testlerinin gereğinden çok daha uzun sürmesine neden olabilir.
SuperTest, yeni bir aktif sunucu oluşturmadan API'leri çağırmak için bir JavaScript kitaplığıdır. TCP istekleri oluşturmak için bir kitaplık olan SuperAgent'ı temel alır. Bu kütüphane ile yeni TCP bağlantıları oluşturmaya gerek yoktur. API'ler neredeyse anında çağrılır.
SuperTest, vaatler için destekle, söz verildiği gibi süper testtir. Böyle bir istek bir söz verdiğinde, birden çok iç içe geri arama işlevinden kaçınmanıza izin vererek akışı yönetmeyi çok daha kolay hale getirir.
const express = require('express') const request = require('supertest-as-promised'); const app = express(); request(app).get("/recipes").then(res => assert(....));
SuperTest, Express.js çerçevesi için yapıldı, ancak küçük değişikliklerle diğer çerçevelerle de kullanılabilir.
Diğer Yardımcı Programlar
Bazı durumlarda, kodumuzdaki bazı bağımlılıklarla alay etmeye, casusları kullanarak işlevler etrafında mantığı test etmeye veya belirli yerlerde taslakları kullanmaya ihtiyaç vardır. Bu yardımcı program paketlerinden bazılarının kullanışlı olduğu yer burasıdır.
SinonJS, testler için casusları, taslakları ve alayları destekleyen harika bir kütüphanedir. Ayrıca, bükülme süresi, test korumalı alanı ve genişletilmiş iddia gibi diğer yararlı test özelliklerini ve ayrıca sahte sunucuları ve istekleri destekler.
Bazı durumlarda, kodumuzdaki bazı bağımlılıklarla alay etmeye ihtiyaç vardır. Taklit etmek istediğimiz hizmetlere yapılan referanslar, sistemin diğer bölümleri tarafından kullanılmaktadır.
Bu sorunu çözmek için bağımlılık enjeksiyonunu kullanabiliriz veya bu bir seçenek değilse Mockery gibi bir alay hizmeti kullanabiliriz.
Mockery, dış bağımlılıkları olan alaycı kodlara yardımcı olur. Düzgün kullanmak için, testler veya kod yüklenmeden önce Mockery çağrılmalıdır.
const mockery = require('mockery'); mockery.enable({ warnOnReplace: false, warnOnUnregistered: false }); const mockingStripe = require('lib/services/internal/stripe'); mockery.registerMock('lib/services/internal/stripe', mockingStripe);
Bu yeni referansla (bu örnekte, mockingStripe
), daha sonra testlerimizde hizmetlerle alay etmek daha kolaydır.
const stubStripeTransfer = sinon.stub(mockingStripe, 'transferAmount'); stubStripeTransfer.returns(Promise.resolve(null));
Sinon kütüphanesinin yardımıyla alay etmek kolaydır. Buradaki tek sorun, bu saplamanın diğer testlere yayılacak olmasıdır. Sandbox için sinon sandbox kullanılabilir. Bununla birlikte, sonraki testler sistemi ilk durumuna geri getirebilir.
const sandbox = require('sinon').sandbox.create(); const stubStripeTransfer = sandbox.sinon.stub(mockingStripe, 'transferAmount'); stubStripeTransfer.returns(Promise.resolve(null)); // after the test, or better when starting a new test sandbox.restore();
Aşağıdaki gibi işlevler için başka bileşenlere ihtiyaç vardır:
- Veritabanının boşaltılması (bir hiyerarşi ön oluşturma sorgusu ile yapılabilir)
- Çalışma durumuna ayarlama (sekans-fikstür)
- 3. taraf hizmetlere yönelik TCP istekleriyle alay etme (nock)
- Daha zengin iddialar kullanma (chai)
- Üçüncü taraflardan kaydedilen yanıtlar (kolay düzeltme)
O kadar basit olmayan testler
Soyutlama ve genişletilebilirlik, etkili bir entegrasyon test takımı oluşturmanın temel unsurlarıdır. Odağı testin özünden uzaklaştıran her şey (verilerinin hazırlanması, eylemi ve iddiası) gruplandırılmalı ve fayda fonksiyonlarında özetlenmelidir.
Burada doğru ya da yanlış bir yol olmamasına rağmen, her şey projeye ve gereksinimlerine bağlı olduğundan, bazı temel nitelikler herhangi bir iyi entegrasyon test paketinde hala ortaktır.
Aşağıdaki kod, bir tarif oluşturan ve yan etki olarak bir e-posta gönderen bir API'nin nasıl test edileceğini gösterir.
Bir e-postanın gerçekten gönderilmeden gönderilip gönderilmeyeceğini test edebilmeniz için harici e-posta sağlayıcısını saplar. Test ayrıca API'nin uygun durum koduyla yanıt verip vermediğini de doğrular.
const co = require('co'); const factory = require('factory'); const superTest = require('../utils/super_test'); const basicEnv = require('../utils/basic_test_enivornment'); const path = '/v1/admin/recipes'; basicEnv.test(`API POST ${path}`, co.wrap(function* (t, assert, sandbox) { const chef = yield factory.create('chef'); const body = { chef_id: chef.id, recipe_name: 'cake', Ingredients: ['carrot', 'chocolate', 'biscuit'] }; const stub = sandbox.stub(mockery.emailProvider, 'sendNewEmail').returnsPromise(null); const serverResponse = yield superTest.get(path, body); assert.spies(stub).called(1); assert.statusCode(serverResponse, 201); }));
Yukarıdaki test, her seferinde temiz bir ortamla başladığı için tekrarlanabilir.
Kurulumla ilgili her şeyin basicEnv.test
işlevi içinde bir araya getirildiği basit bir kurulum sürecine sahiptir.
Yalnızca bir eylemi test eder - tek bir API. Ve testin beklentilerini basit assert ifadeleriyle açıkça belirtir. Ayrıca, test, saplama/alay etme yoluyla üçüncü taraf kodunu içermez.
Entegrasyon Testleri Yazmaya Başlayın
Yeni kodu üretime aktarırken, geliştiriciler (ve diğer tüm proje katılımcıları) yeni özelliklerin çalışacağından ve eskilerinin bozulmadığından emin olmak isterler.
Bunu test etmeden elde etmek çok zordur ve kötü yapılırsa hayal kırıklığına, proje yorgunluğuna ve sonunda projenin başarısız olmasına neden olabilir.
Birim testleri ile birleştirilen entegrasyon testleri, ilk savunma hattıdır.
İkisinden sadece birini kullanmak yetersizdir ve ortaya çıkarılan hatalar için çok fazla alan bırakacaktır. Daima her ikisini de kullanmak, yeni taahhütleri sağlamlaştıracak ve tüm proje katılımcılarına güven verecek ve güven aşılayacaktır.