Node.js + REST + Testing + Code coverage


აქამდე Node.js არასდროს გამომიყენებია production გარემოში. ახლა ერთ-ერთ პროექტზე გადავწყვიტეთ მისი ცდა და მინდოდა პატარა კვლევები აქაც გამომექვეყნებინა. რომ გავუშვებთ, შედეგებზე და პრობლემებზე მერე დავწერ.

ზოგადად, ერთი ხელის მოსმით რთული გასაკეთებელია არჩევანი. იმის მიუხედავად, რომ ჯავასკრიპტზეც არც ისე ცოტა მიწერია, არასდროს ისე მყარად არ ვგრძნობ თავს, როგორც ჯავაში ან სხვა strongly typed ენაში. მექანიკური შეცდომების პოვნა საკმაოდ რთულია და არც ვიცი ხოლმე გადავაწყდები თუ არა როდესმე ტესტირებისას. შეცდომების მართვა (Error handling) ცალკე თავისტკივილია, რადგან შეიძლება ერთმა შეცდომამ მთელი სერვისი მოკლას. ბიბლიოთეკებს შორის არ არის მათი სტაბილური და ერთნაირი დამუშავება.

რაღაც მხრივ, node-ზე მაინც საყვარლად და კომპაქტურად იწერება სერვისები. არის პრეცედენტები, რომ დიდი კომპანიები გადავიდნენ node-ზე და ჯერჯერობით თავს ართმევენ (მაგ: Paypal). ჩვენს შემთხვევაში სოკეტები და ბრაუზერის ჯავასკრიპტი გვჭირდებოდა ინტენსიურად, ამიტომ ბოლოს ისევ node-ზე გავჩერდით.

მაშ ასე: ამოცანა არის REST API-ის გაკეთება.
ყველა პატარა კლასის თუ ფუნქციის გამო მოდულების ჩასმა არ მიყვარს, რადგან თითოეული dependency პოტენციური პრობლემაა. ყველაფრის ნოლიდან წერასაც არ ვაპირებ, ამიტომ ვცდილობ ბალანსი დავიცვა და მინიმალური რაოდენობის და ფუნქციების მოდულები გამოვიყენო.

მარშრუტებისთვის (routing) Restify-ზე შევჩერდი. ვებ საიტისთვის Express კარგიაო, მაგრამ სერვისების შემთხვევაში ბევრი რამე უფრო მარტივად არის, ამიტომ Restify საკმარისი ჩანს.

ტესტირებისთვის Mocha ავიღე. Mocha-ს ნებისმიერ assertion ბიბლიოთეკასთან შეუძლია მუშაობა. სტატიებში შეგხვდებათ ტუტორიალები – Should.js, Chai, expect.js, better-assert, unexpected.. მაგრამ ჩემი აზრით სულ ზედმეტია ეგ ყველაფერი. Node-ის ჩაშენებული assert მოდული მშვენივრად ართმევს თავს ყველაფერს, სხვა დანარჩენი კი უბრალოდ სიტყვების გადალაგება-გადმოლაგებაა.

ადრე ტესტებს საერთოდ არ ვწერდი. რაღაცნაირად უმეტესად ისეთ პროექტებზე ვხვდებოდი, რომელიც ან საიტი იყო ან წებო სხვადასხვა სისტემას შორის, ამიტომ ავტომატური ტესტირება არ გამოდიოდა. ახლა ვხვდები, რომ ტესტების გარეშე არ შემიძლია. შეიძლება იფიქროთ, რომ მეტ დროს გახარჯინებთ, მაგრამ პირიქით ძალიან ბევრ დროს ზოგავს და რუტინულ სამუშაოს გაცილებთ თავიდან. განსაკუთრებით მადლობას ეტყვით საკუთარ თავს ცვლილებების დროს (Regression testing), რადგან ერთი ბაგის გასწორების დროს ხშირად ბევრ ახალს ვაჩენთ სხვა ადგილებში.

ტესტების დამხმარედ არსებობს Code coverage ხელსაწყოები, რომელიც გვაჩვენებს ჩვენმა ტესტებმა კოდის რა ნაწილები დაფარა. ჯავასკრიპტზეც არის რამდენიმე, მე Instanbul-ზე შევჩერდი, რადგან ბევრი აქებდა.

Unit ტესტებისთვის ეს საკმარისია, თუმცა მე API-ის გატესტვაც მინდა და ამისთვის რამე პატარა http client მჭირდება. ცხადია, ცარიელი node-ითაც შეიძლება გაკეთება, მაგრამ მოდი კიდევ ერთ მოდულს დავამატებ სრული კომფორტისთვის – Supertest.

ამ პოსტში ყველაფერს ერთად ავამუშავებ. ოღონდ აუცილებლად უნდა აღვნიშნო, რომ ნამდვილი პროექტი უკეთ უნდა იყოს სტრუქტურირებული. მარშრუტები სხვა ფაილში, კონტროლერების ლოგიკა ცალკე, მოდელები და ბაზის ფენა ცალკე.. ამ მაგალითებში ერთ ადგილას არის მთელი სერვისი და სერვერის კონფიგურაცია-გაშვებაც.

პროექტის შექმნა

პირველ რიგში ინსტალირებული უნდა გქონდეთ Node.js და npm (პაკეტების მენეჯერი). ბრძანებების გაშვება ხდება ტერმინალში.

გადავიდეთ პროექტის საქაღალდეში და გავუშვათ ბრძანება

npm init

ეს რამდენიმე კითხვას დაგისვამთ და შედეგად შექმნის package.json ფაილს, რომელშიც მომავალში ჩვენი dependence-ები აღიწერება.

მოდულების ინსტალაცია

ისევ დავდგეთ პროექტის საქაღალდეში და გავუშვათ ბრძანება

npm install restify --save

შეიქმნება node_modules საქაღალდე და იქ გადმოიწერება მოდული. –save პარამეტრის შედეგად კი ჩვენს package.json-ში ჩაემატება.

ეს იმიტომ არის საჭირო, რომ მაგალითად node_modules საქაღალდეს პროექტს თან არ აყოლებენ ხოლმე, თუნდაც version control სისტემაში. ვისაც პროექტის გაშვება დასჭირდება, package.json ეყოფა, რომ საჭირო dependence-ები ჩამოტვირთოს.

ანალოგიურად გადმოვწეროთ Mocha, Instanbul და supertest:

npm install mocha --save-dev
npm install nyc --save-dev
npm install supertest --save-dev

ამ შემთხვევაში –save-dev-ს ვიყენებ, რადგან ეს ბიბლიოთეკები მხოლოდ დეველოპმენტის პროცესშია საჭირო და Production გარემოში მათი ინსტალაცია ზედმეტია.

სერვერის შექმნა და მარშრუტების გაწერა

შევქმნი მარტივ GET სერვისს, რომელსაც მისამართში გადმოეცემა name პარამეტრი და მისალმების ტექსტს აბრუნებს პასუხად. თუ კლიენტმა application/json ფორმატი მოითხოვა, json ობიექტად დააბრუნებს, სხვა შემთხვევაში კი ჩვეულებრივ ტექსტად.

index.js ფაილი

var restify = require('restify');

// შევქმნათ restify-ის სერვერი და აღვწეროთ მარშრუტები
var server = restify.createServer();
server.get('/hello/:name', sayHello);
server.head('/hello/:name', sayHello);

// ფუნქცია მოთხოვნის დასამუშავებლად
function sayHello(req, res, next) {
    let content;
    let name = req.params.name;

    // თუ json-ს ითხოვს კლიენტი, json ობიექტი დაუბრუნდეს
    // წინააღმდეგ შემთხვევაში ჩვეულებრივი ტექსტი
    if (req.headers.accept.match(/json/i)) {
        content = { hello: name };
        // Restify ავტომატურად json-ს დააბრუნებს ასეთ დროს
    } else {
        content = 'hello ' + name;
        res.header('Content-Type','text/plain');
    }
    res.send(content);
    next();
}

// გავუშვათ სერვერი
server.listen(8080, function() {
  console.log('%s listening at %s', server.name, server.url);
});

// ექსპორტი საჭიროა, რომ შემდეგ Mocha-დან მივწვდეთ სერვერს.
module.exports = server;

ტესტების შექმნა

პროექტის საქაღალდეში შევქმნათ საქაღალდე ‘test’ და მასში ჩავწეროთ ნებისმიერი სახელის ფაილი ტესტებისთვის. ასეთი ფაილები ნებისმიერი რაოდენობით შეიძლება გვქონდეს.
test/testHello.js

let request = require('supertest');
let server = require('../index');
let assert = require('assert');

describe('Hello', function () {
    
    it('should say hello', done => {
        request(server)
            .get('/hello/elle')
            .set('Accept', 'text/plain')
            .expect('Content-type', 'text/plain')
            .expect(200, "hello elle", done);
    });

    it('should say hello with json', done => {
        request(server)
            .get('/hello/elle')
            .set('Accept', 'application/json')
            .expect('Content-Type', /json/)
            .expect(200, {
                hello: "elle"
            }, done);
    });
});

შემდეგ package.json-ის scripts-ში გავწეროთ ტესტის გასაშვები ბრძანება:

"scripts": {
  "test": "mocha"
}

შედეგად პროექტის საქაღალდიდან შეგვიძლია გავუშვათ ბრძანება

npm test

ასე გამოიყურება ტესტის შედეგი ჩემთან:

მოდი დავამატოთ code coverage. package.json-ში “test”: “mocha” შევცვალოთ შემდეგით: “test”: “nyc mocha” და ისევ გავუშვათ npm test.
ჩემს მაგალითზე კონსოლში გამოიტანა:

ახლა სრულად არის კოდი დაფარული ტესტებით. რაიმე ზედმეტ ფუნქციას ჩავამატებ და Uncovered Lines რაოდენობა გაიზრდება, თან ტერმინალში კარგად არ ჩანს შედეგი. html-ით რეპორტინგი გამოვიყენოთ. package.json-ში კვლავ შევცვალოთ კონფიგურაცია შემდეგით:

"test": "nyc --reporter=html --reporter=text mocha"

და კვლავ გავუშვათ npm test
პროექტის საქაღალდეში დაინახავთ, რომ შეიქმნა ახალი დირექტორია – coverage და მასში არის რეპორტის ფაილები. მაგალითად, ჩემი index.js ფაილის ანგარიში ასე გამოიყურება:

თუ ვერსიის კონტროლს იყენებთ, coverage და .nyc_output საქაღალდეები უმჯობესია .gitignore ფაილში დაამატოთ.

ერთიანი პროექტის გადმოწერა

პროექტის საქაღალდეში არ გამოვაყოლე მოდულები. შეგიძლიათ გაუშვათ npm install ბრძანება პროექტის დირექტორიიდან და package.json-ის მიხედვით დააინსტალირებს საჭირო მოდულებს იმავე დირექტორიაში.

გამოხმაურება

comments

2 thoughts on “Node.js + REST + Testing + Code coverage

  • 09 სექტემბერი, 2017 at 13:04
    Permalink

    “შეცდომების მართვა (Error handling) ცალკე თავისტკივილია, რადგან შეიძლება ერთმა შეცდომამ მთელი სერვისი მოკლას.”

    — თუ შეგიძლიათ განავრცოთ ზემოთა წინადადება რა იგულისხმება მანდ და რა გზებია მაგისი თავიდან ასარიდებლად რო ერთმა შეცდომამ სერვისი არ მოკლას?

    Reply
    • 06 ნოემბერი, 2017 at 17:43
      Permalink

      ბოდიში, გვიან გპასუხობთ. ნოდის შეცდომების მართვაზე ერთი კარგი სტატია შემხვდა, შეიძლება თქვენც მოგეწონოთ:

      Error Handling in Node.js
      https://www.joyent.com/node-js/production/design/errors

      Reply

გაიარეთ ავტორიზაცია კომენტარის დასამატებლად: admin კომენტარის გაუქმება

თქვენი ელფოსტის მისამართი გამოქვეყნებული არ იყო. აუცილებელი ველები მონიშნულია *