Node Hero: Частина 9 - модульне тестування у Node.js

6 хв. читання

Тестування Node.js-застосунків

Ви можете вважати тести гарантією для застосунків, які ви розробляєте. Тести будуть працювати не тільки на вашому локальному комп'ютері, але і у сервісах неперервної інтеграції, так, щоб версії з помилками не потрапили до виробничих систем.

Що ми повинні тестувати у нашому застосуванні? Скільки тестів ми повинні зробити?

Відповідь залежить від сценаріїв використання, але, як правило, ви можете використовувати піраміду тестування.

Node.js

По суті, піраміда тестування описує, що ви повинні написати модульні тести, тести інтеграції та end-to-end(нерозривні) тести. Модульних тестів повинно бути більше, ніж тестів інтеграції, яких в свою чергу повинно бути більше, ніж end-to-end тестів.

Давайте розглянемо, як ви можете додавати модульні тести для ваших додатків!

Зверніть увагу, що тут ми не будемо говорити про тести інтеграції та end-to-end тести, оскільки вони виходять далеко за рамки даного туторіалу.


Модульне тестування Node.js-застосунків

Модульні тести пишуться для того, щоб перевірити, чи працює відповідний модуль. Ми будемо використовувати підроблені залежності.

Тести потрібно писати для відкритих методів, а не для внутрішньої роботи відповідного модуля.

Анатомія модульного тесту

Кожний модульний тест має наступну структуру:

  1. Встановлення тесту
  2. Виклик тестованого методу
  3. Затвердження

Кожен модульний тест повинен тестувати лише одну функцію.

Модулі, які використовуються для Модульного Тестування

Для модульного тестування, ми будемо використовувати наступні модулі:

  • виконавець тестів: mocha, альтернативою є tape

  • бібліотека для затвердження: chai, альтернативою є модуль assert

  • тести spies(шпигуни), stubs(заглушки) та mocks(імітації): sinon.

Spies(шпигуни), stubs(заглушки) та mocks(імітації) - що та коли?

Перед тим як запускати тестування, давайте розглянемо, що таке spies, stubs та mocks!

Spies

Ви можете використовувати spies, щоб отримати інформацію щодо викликів функцій, наприклад скільки разів вони були викликані і які аргументи їм передавалися.

it('calls subscribers on publish', function () {  
  var callback = sinon.spy()
  PubSub.subscribe('message', callback)

  PubSub.publishSync('message')

  assertTrue(callback.called)
})
// приклад було взято з документації sinon: http://sinonjs.org/docs/

Stubs

Stubs схожі на spies, але вони замінюють цільову функцію. Ви можете використовувати stubs, щоб контролювати поведінку методу та змусити код видати помилку, або щоб запобігти безпосередній виклик інших ресурсів (наприклад HTTP API).

it('calls all subscribers, even if there are exceptions', function (){  
  var message = 'an example message'
  var error = 'an example error message'
  var stub = sinon.stub().throws()
  var spy1 = sinon.spy()
  var spy2 = sinon.spy()

  PubSub.subscribe(message, stub)
  PubSub.subscribe(message, spy1)
  PubSub.subscribe(message, spy2)

  PubSub.publishSync(message, undefined)

  assert(spy1.called)
  assert(spy2.called)
  assert(stub.calledBefore(spy1))
})
// приклад було взято з документації sinon: http://sinonjs.org/docs/

Mocks

Mocks - це імітаційні методи з попередньо запрограмованими поведінкою та очікуванням.

it('calls all subscribers when exceptions happen', function () {  
  var myAPI = { 
    method: function () {} 
  }

  var spy = sinon.spy()
  var mock = sinon.mock(myAPI)
  mock.expects("method").once().throws()

  PubSub.subscribe("message", myAPI.method)
  PubSub.subscribe("message", spy)
  PubSub.publishSync("message", undefined)

  mock.verify()
  assert(spy.calledOnce)
// приклад було взято з документації sinon: http://sinonjs.org/docs/
})

Як ви можете бачити, для mocks ви повинні задати очікування.


Представимо, що потрібно протестувати наступний модуль:

const fs = require('fs')  
const request = require('request')

function saveWebpage (url, filePath) {  
  return getWebpage(url, filePath)
    .then(writeFile)
}

function getWebpage (url) {  
  return new Promise (function (resolve, reject) {
    request.get(url, function (err, response, body) {
      if (err) {
        return reject(err)
      }

      resolve(body)
    })
  })
}

function writeFile (fileContent) {  
  let filePath = 'page'
  return new Promise (function (resolve, reject) {
    fs.writeFile(filePath, fileContent, function (err) {
      if (err) {
        return reject(err)
      }

      resolve(filePath)
    })
  })
}

module.exports = {  
  saveWebpage
}

Цей модуль робить одне: він зберігає веб-сторінку (базуючись на даному URL) у файл на локальній машині. Щоб протестувати цей модуль нам потрібно використати stub на модулях fs та request.

Перед тим як писати модульні тести для цього модуля, у RisingStack, ми зазвичай додаємо файл test-setup.spec.js, щоб налаштувати базові тести, наприклад створення sinon sandbox. Це дасть вам можливість на писати sinon.sandbox.create() та sinon.sandbox.restore() після кожних тестів.

// test-setup.spec.js
const sinon = require('sinon')  
const chai = require('chai')

beforeEach(function () {  
  this.sandbox = sinon.sandbox.create()
})

afterEach(function () {  
  this.sandbox.restore()
})

Зверніть увагу, що ми завжди розташовуємо тести разом з реалізацією, тому вони мають .spec.js. У нашому package.json ви знайдете ці рядки:

{
  "test-unit": "NODE_ENV=test mocha '/**/*.spec.js'",
}

Тепер час писати самі тести!

const fs = require('fs')  
const request = require('request')

const expect = require('chai').expect

const webpage = require('./webpage')

describe('The webpage module', function () {  
  it('saves the content', function * () {
    const url = 'google.com'
    const content = '<h1>title</h1>'
    const writeFileStub = this.sandbox.stub(fs, 'writeFile', function (filePath, fileContent, cb) {
      cb(null)
    })

    const requestStub = this.sandbox.stub(request, 'get', function (url, cb) {
      cb(null, null, content)
    })

    const result = yield webpage.saveWebpage(url)

    expect(writeFileStub).to.be.calledWith()
    expect(requestStub).to.be.calledWith(url)
    expect(result).to.eql('page')
  })
})

Повний код можна знайти тут: https://github.com/RisingStack/nodehero-testing

Покриття коду

Щоб зрозуміти, на скільки добре ви покрили ваш код тестами, ви можете згенерувати звіт.

Цей звіт буде включати:

  • Лінійте покриття

  • Покриття інструкцій

  • Покриття гілкок

  • Покриття функцій

У RisingStack ми використовуємо istanbul для покриття коду. Вам потрібно додати наступний скрипт у ваш package.json, щоб використовувати istanbul з mocha:

istanbul cover _mocha $(find ./lib -name \"*.spec.js\" -not -path \"./node_modules/*\")

Після цього ви отримаєте наступне:

Node.js

Далі: Частина 10 - Відлагодження Node.js-застосунків
Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.8K
Приєднався: 8 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація