Node.js ve MongoDB ile Entegrasyon ve Uçtan Uca Testler Kolaylaştı

Yayınlanan: 2022-03-11

Testler, sağlam bir Node.js uygulaması oluşturmanın önemli bir parçasıdır. Doğru testler, geliştiricilerin Node.js geliştirme çözümleri hakkında belirtebilecekleri birçok eksikliği kolayca giderebilir.

Birçok geliştirici birim testleri ile %100 kapsamaya odaklanırken, yazdığınız kodun yalnızca tek başına test edilmemesi önemlidir. Entegrasyon ve uçtan uca testler, uygulamanızın parçalarını birlikte test ederek size bu ekstra güveni verir. Bu parçalar kendi başlarına gayet iyi çalışıyor olabilir, ancak büyük bir sistemde kod birimleri nadiren ayrı ayrı çalışır.

Node.js ve MongoDB birlikte son zamanların en popüler ikililerinden birini oluşturuyor. Onları kullanan birçok insandan biriyseniz, şanslısınız.

Bu makalede, veritabanının gerçek örneklerinde çalışan Node.js ve MongoDB uygulamanız için ayrıntılı bir ortam veya karmaşık kurulum/teardown kodu kurmanıza gerek kalmadan entegrasyon ve uçtan uca testlerin nasıl kolayca yazılacağını öğreneceksiniz. .

Mongo-unit paketinin Node.js'de entegrasyon ve uçtan uca test konusunda nasıl yardımcı olduğunu göreceksiniz. Node.js entegrasyon testlerine daha kapsamlı bir genel bakış için bu makaleye bakın.

Gerçek Bir Veritabanıyla Başa Çıkmak

Tipik olarak, entegrasyon veya uçtan uca testler için komut dosyalarınızın test amacıyla gerçek bir özel veritabanına bağlanması gerekir. Bu, veritabanının temiz, öngörülebilir bir durumda olmasını sağlamak için her test senaryosunun/paketinin başında ve sonunda çalışan kod yazmayı içerir.

Bu, bazı projeler için işe yarayabilir, ancak bazı sınırlamaları vardır:

  • Test ortamı oldukça karmaşık olabilir. Veritabanını bir yerde çalışır durumda tutmanız gerekecek. Bu genellikle CI sunucularıyla kurulum için ekstra çaba gerektirir.
  • Veritabanı ve işlemler nispeten yavaş olabilir. Veritabanı ağ bağlantılarını kullanacağından ve işlemler dosya sistemi etkinliği gerektireceğinden, binlerce testi hızlı bir şekilde çalıştırmak kolay olmayabilir.
  • Veritabanı durumu tutar ve testler için çok uygun değildir. Testler birbirinden bağımsız olmalıdır, ancak ortak bir DB kullanmak, bir testin diğerlerini etkilemesine neden olabilir.

Öte yandan, gerçek bir veritabanı kullanmak, test ortamını mümkün olduğunca üretime yakın hale getirir. Bu, bu yaklaşımın özel bir avantajı olarak görülebilir.

Gerçek, Bellek İçi Bir Veritabanı Kullanma

Test için gerçek bir veritabanı kullanmanın bazı zorlukları var gibi görünüyor. Ancak, gerçek bir veritabanı kullanmanın avantajı aktarılamayacak kadar iyidir. Zorluklar üzerinde nasıl çalışabilir ve avantajı nasıl koruyabiliriz?

Başka bir platformdan iyi bir çözümü yeniden kullanmak ve onu Node.js dünyasına uygulamak, buraya gitmenin yolu olabilir.

Java projeleri, bu amaç için bir bellek içi veritabanıyla (örn., H2) DBUnit'i yaygın olarak kullanır.

DBUnit, JUnit (Java test çalıştırıcısı) ile entegredir ve her test/test takımı vb. için veritabanı durumunu tanımlamanıza izin verir. Yukarıda tartışılan kısıtlamaları ortadan kaldırır:

  • DBUnit ve H2, Java kitaplıklarıdır, bu nedenle ekstra bir ortam kurmanıza gerek yoktur. Hepsi JVM'de çalışır.
  • Bellek içi veritabanı, bu durum yönetimini çok hızlı hale getirir.
  • DBUnit, veritabanı yapılandırmasını çok basit hale getirir ve her durum için açık bir veritabanı durumu tutmanıza olanak tanır.
  • H2 bir SQL veritabanıdır ve MySQL ile kısmen uyumludur, bu nedenle büyük durumlarda uygulama onunla bir üretim veritabanında olduğu gibi çalışabilir.

Bu kavramlardan yola çıkarak Node.js ve MongoDB: Mongo-unit için benzer bir şey yapmaya karar verdim.

Mongo-unit, NPM veya Yarn kullanılarak kurulabilen bir Node.js paketidir. MongoDB'yi bellekte çalıştırır. Mocha ile iyi bir şekilde bütünleşerek ve veritabanı durumunu yönetmek için basit bir API sağlayarak entegrasyon testlerini kolaylaştırır.

Kitaplık, popüler işletim sistemleri için önceden oluşturulmuş MongoDB ikili dosyalarını içeren mongodb önceden oluşturulmuş NPM paketini kullanır. Bu MongoDB örnekleri bellek içi modda çalışabilir.

Mongo ünitesini kurma

Projenize mongo birimi eklemek için şunları çalıştırabilirsiniz:

 npm install -D mongo-unit

veya

 yarn add mongo-unit

Ve işte bu. Bu paketi kullanmak için bilgisayarınızda MongoDB kurulu olmasına bile gerek yok.

Entegrasyon Testleri için Mongo birimini kullanma

Görevleri yönetmek için basit bir Node.js uygulamanız olduğunu düşünelim:

 // service.js const mongoose = require('mongoose') const mongoUrl = process.env.MONGO_URL || 'mongodb://localhost:27017/example' mongoose.connect(mongoUrl) const TaskSchema = new mongoose.Schema({ name: String, started: Date, completed: Boolean, }) const Task = mongoose.model('tasks', TaskSchema) module.exports = { getTasks: () => Task.find(), addTask: data => new Task(data).save(), deleteTask: taskId => Task.findByIdAndRemove(taskId) }

MongoDB bağlantı URL'si burada sabit kodlanmamıştır. Çoğu web uygulaması arka ucunda olduğu gibi, onu ortam değişkeninden alıyoruz. Bu, testler sırasında herhangi bir URL ile değiştirmemize izin verecektir.

 const express = require('express') const bodyParser = require('body-parser') const service = require('./service') const app = express() app.use(bodyParser.json()) app.use(express.static(`${__dirname}/static`)) app.get('/example', (req, res) => { service.getTasks().then(tasks => res.json(tasks)) }) app.post('/example', (req, res) => { service.addTask(req.body).then(data => res.json(data)) }) app.delete('/example/:taskId', (req, res) => { service.deleteTask(req.params.taskId).then(data => res.json(data)) }) app.listen(3000, () => console.log('started on port 3000'))

Bu, kullanıcı arayüzüne sahip örnek bir uygulamanın bir parçası. Kullanıcı arayüzünün kodu, kısa olması için çıkarılmıştır. GitHub'da tam örneğe göz atabilirsiniz.

Mocha ile entegrasyon

Mocha'nın mongo-unite karşı entegrasyon testleri çalıştırmasını sağlamak için, uygulama kodu Node.js bağlamında yüklenmeden önce mongo-unit veritabanı örneğini çalıştırmamız gerekir. Bunu yapmak için mocha --require parametresini ve gerekli komut dosyalarında eşzamansız işlemler gerçekleştirmenize izin veren Mocha-prepare kitaplığını kullanabiliriz.

 // it-helper.js const prepare = require('mocha-prepare') const mongoUnit = require('mongo-unit') prepare(done => mongoUnit.start() .then(testMongoUrl => { process.env.MONGO_URL = testMongoUrl done() }))

Entegrasyon Testleri Yazma

İlk adım, test veritabanına bir test eklemektir ( testData.json ):

 { "tasks": [ { "name": "test", "started": "2017-08-28T16:07:38.268Z", "completed": false } ] }

Bir sonraki adım, testleri kendileri eklemektir:

 const expect = require('chai').expect const mongoose = require('mongoose') const mongoUnit = require('../index') const service = require('./app/service') const testMongoUrl = process.env.MONGO_URL describe('service', () => { const testData = require('./fixtures/testData.json') beforeEach(() => mongoUnit.initDb(testMongoUrl, testData)) afterEach(() => mongoUnit.drop()) it('should find all tasks', () => { return service.getTasks() .then(tasks => { expect(tasks.length).to.equal(1) expect(tasks[0].name).to.equal('test') }) }) it('should create new task', () => { return service.addTask({ name: 'next', completed: false }) .then(task => { expect(task.name).to.equal('next') expect(task.completed).to.equal(false) }) .then(() => service.getTasks()) .then(tasks => { expect(tasks.length).to.equal(2) expect(tasks[1].name).to.equal('next') }) }) it('should remove task', () => { return service.getTasks() .then(tasks => tasks[0]._id) .then(taskId => service.deleteTask(taskId)) .then(() => service.getTasks()) .then(tasks => { expect(tasks.length).to.equal(0) }) }) })

Ve işte!

Kurulum ve sökme ile ilgili sadece birkaç satır kod olduğuna dikkat edin.

Gördüğünüz gibi mongo-unit kütüphanesini kullanarak entegrasyon testleri yazmak çok kolay. MongoDB'nin kendisiyle alay etmiyoruz ve aynı Mongoose modellerini kullanabiliriz. Veritabanı verileri üzerinde tam kontrole sahibiz ve sahte MongoDB bellekte çalıştığı için test performanslarında fazla bir şey kaybetmeyiz.

Bu aynı zamanda entegrasyon testleri için en iyi birim testi uygulamalarını uygulamamıza da olanak tanır:

  • Her testi diğer testlerden bağımsız yapın. Her testten önce yeni veriler yükleyerek bize her test için tamamen bağımsız bir durum veririz.
  • Her test için minimum gerekli durumu kullanın. Tüm veritabanını doldurmamıza gerek yok. Her belirli test için yalnızca gerekli minimum verileri ayarlamamız gerekir.
  • Veritabanı için bir bağlantıyı yeniden kullanabiliriz. Test performansını artırır.

Bir bonus olarak, uygulamanın kendisini mongo birimine karşı bile çalıştırabiliriz. Sahte bir veritabanına karşı uygulamamız için uçtan uca testler yapmamızı sağlar.

Selenyum ile Uçtan Uca Testler

Uçtan uca testler için Selenium WebDriver ve Hermione E2E test çalıştırıcısını kullanacağız.

İlk olarak, sürücüyü ve test çalıştırıcısını önyükleyeceğiz:

 const mongoUnit = require('mongo-unit') const selenium = require('selenium-standalone') const Hermione = require('hermione') const hermione = new Hermione('./e2e/hermione.conf.js') //hermione config seleniumInstall() //make sure selenium is installed .then(seleniumStart) //start selenium web driver .then(mongoUnit.start) // start mongo unit .then(testMongoUrl => { process.env.MONGO_URL = testMongoUrl //store mongo url }) .then(() => { require('./index.js') //start application }) .then(delay(1000)) // wait a second till application is started .then(() => hermione.run('', hermioneOpts)) // run hermiona e2e tests .then(() => process.exit(0)) .catch(() => process.exit(1))

Ayrıca bazı yardımcı işlevlere de ihtiyacımız olacak (kısa olması için hata işleme kaldırıldı):

 function seleniumInstall() { return new Promise(resolve => selenium.install({}, resolve)) } function seleniumStart() { return new Promise(resolve => selenium.start(resolve)) } function delay(timeout) { return new Promise(resolve => setTimeout(resolve, timeout)) }

Veritabanını bazı verilerle doldurduktan ve testler yapıldıktan sonra temizledikten sonra ilk testlerimizi çalıştırabiliriz:

 const expect = require('chai').expect const co = require('co') const mongoUnit = require('../index') const testMongoUrl = process.env.MONGO_URL const DATA = require('./fixtures/testData.json') const ui = { task: '.task', remove: '.task .remove', name: '#name', date: '#date', addTask: '#addTask' } describe('Tasks', () => { beforeEach(function () { return mongoUnit.initDb(testMongoUrl, DATA) .then(() => this.browser.url('http://localhost:3000')) }) afterEach(() => mongoUnit.dropDb(testMongoUrl)) it('should display list of tasks', function () { const browser = this.browser return co(function* () { const tasks = yield browser.elements(ui.task) expect(tasks.length, 1) }) }) it('should create task', function () { const browser = this.browser return co(function* () { yield browser.element(ui.name).setValue('test') yield browser.element(ui.addTask).click() const tasks = yield browser.elements(ui.task) expect(tasks.length, 2) }) }) it('should remove task', function () { const browser = this.browser return co(function* () { yield browser.element(ui.remove).click() const tasks = yield browser.elements(ui.task) expect(tasks.length, 0) }) }) })

Gördüğünüz gibi uçtan uca testler entegrasyon testlerine çok benziyor.

Sarmak

Tüm büyük ölçekli uygulamalar için entegrasyon ve uçtan uca testler önemlidir. Özellikle Node.js uygulamaları otomatik testlerden büyük ölçüde yararlanabilir. Mongo-unit ile, bu tür testlerin getirdiği tüm zorluklar hakkında endişelenmeden entegrasyon ve uçtan uca testler yazabilirsiniz.

GitHub'da mongo biriminin nasıl kullanılacağına dair eksiksiz örnekler bulabilirsiniz.

Toptal Mühendislik Blogunda Daha Fazla Okuma:

  • Node.js/TypeScript REST API Oluşturma, Bölüm 3: MongoDB, Kimlik Doğrulama ve Otomatik Testler