function(words) {
    return '#' + words
        .split(/\s+/)
        .map(normalizeWord)
        .join('');
}
        

Это тоже удовольствие!


function() {
    var actual = hashTag('hello FDConf');

    actual.should.be.equal('#HelloFdconf');
}
        

Тесты - это продуктивно

Придает уверенности

Old web application

Тесты - это продуктивно

Придает уверенности

New web application

Тесты - это продуктивно

Придает уверенности

Помогает рефакторить

Обновлять зависимости

Быть командой

Живая документация

Часть I

Тестирование модулей

Билдер хештегов


FDConf 2016            -> #Fdconf2016
Тестов много не бывает -> #ТестовМногоНеБывает
        

function hashTagGenerator(words) {
    return '#' + words
            .split(/\s+/)
            .map(normalizeWord)
            .join('');
}
        

function normalizeWord(word) {
    return word.charAt(0).toUpperCase() +
        word.toLowerCase().slice(1);
}
        

module.exports = hashTagGenerator;
        

nodejs.org

$ npm install mocha

app/
└── src
    └── hashTagGenerator.js
    └── ...
└── test
    └── hashTagGenerator-test.js
    └── ...
        


var hashTag = require('../src/hashTagGenerator.js');
        
 
describe('Hash tag generator', function () {
    it('should normalize words', function () {
        var actual = hashTag('hello FDConf');
        actual.should.be.equal('#HelloFdconf');
    });
});

chaijs


$ npm install chai
        


var chai = require('chai');
chai.should();
         

$ mocha test
        

  Hash tag generator
     should normalize words

1 passing (3ms)

как ребенок

А что, если...


it('should clean extra symbols', function () {
    var actual = hashTag('    #@mu-ha-ha!!!');
    actual.should.be.equal('#MuHaHa');
});
        
  Hash tag generator
     should start with #
     should concat words
     should normalize words
    1) should clean extra symbols

3 passing (13ms)
1 failing

1) Hash tag generator should clean extra symbols:

    AssertionError: expected '##@%mu-ha-ha!'
                    to equal '#MuHaHa'
    + expected - actual

    -##@%mu-ha-ha!
    +#MuHaHa
    

function hashTagGenerator(words) {
    return '#' + words
            .split(/\s+/)
            .map(normalizeWord)
            .join('');
}
        

function hashTagGenerator(words) {
    return '#' + words
            .split(/[^\wа-яё]/i)
            .map(normalizeWord)
            .join('');
}
        

... под другим углом

Часть II

Тестирование клиентского кода


describe('Twitter signup', function () {
   it('should alert when`twitterok`', function () {
      inputTextTo($('#full-name'), 'twitterok');

      $('.notwitter').is(':visible').should.be.true;
   });
});
        

function inputTextTo($el, text) {
    $el.val(text).trigger('input');
}
        


function inputTextTo($el, text) {
    $el.focus();
    document.execCommand(
        'insertText', false, text);
}
        

<head>


<!-- подключаем стили Mocha -->
<link
    rel="stylesheet"
    href="./node_modules/mocha/mocha.css">
        

<body>


<!-- подключаем библиотеки -->
<script src="./node_modules/mocha/mocha.js"></script>
<script src="./node_modules/chai/chai.js"></script>
        

<!-- настраиваем Mocha -->
<script> mocha.setup('bdd'); </script>
        

<!-- подключаем файл с тестами -->
<script src="/form-test.js"></script>
        

<!-- элемент в котором будут результаты тестов -->
<div id="mocha"></div>
        

<!-- запускаем Mocha -->
<script> mocha.run(); </script>
        

DEMO

Автоматизировать?

PhantomJS

PhantomJS is a headless WebKit scriptable with a JavaScript API.

mocha-phantomjs


            $ npm install -g mocha-phantomjs
        

Адаптируем запуск


<!-- запускаем Mocha -->
<script>
    window.mochaPhantomJS ?
        mochaPhantomJS.run() :
        mocha.run();
</script>
        
$ mocha-phantomjs twitter-signup.html

  Twitter signup
     should no error when input is empty
     should no error when input `teremok`
     should show error when input `twitterok`

  3 passing (14ms)
    
Karma

Часть III

Тестирование сценариев

Перейти на главную страницу https://github.com


В строке поиска набрать "Слайды тестирование FDConf"


Проверить что репозиторий есть в списке

WebdriverIO


            $ npm install webdriverio
        

            $ wdio config
        


wdio.conf.js

describe('GitHub', function () {
   it('search', function () {
      browser.url('http://github.com');
      browser.setValue('input[name="q"]',
                       'Слайды тестирование FDConf');
      browser.submitForm('form[action="/search"]');
      var repoName = browser.getText('h3');
      repoName.should.be
            .equal('Zhigalov/fdconf-tests-slides');
   });
});
            wdio wdio.conf.js
        

DEMO

Запускайте чаще

В IDE

При сохранении файла

Перед коммитом и пушем (husky)

CI сервер ( TeamCity, drone.io, Travis CI, ...)

Travis CI

└── .travis.yml


language: node_js
node_js:
    - "4.1"
        

└── .travis.yml
└── package.json
        

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

DEMO

Информировать команду

  • Письмо
  • SMS сообщение
  • Или как-то еще... ;)

Подменяйте зависимости


var translate = require('translate');
        


function hashTagGenerator(words) {
    return '#' + translate(words)
        .split(/\s+/)
        .map(normalizeWord)
        .join('');
}
        
it('should translate hashTag', function (){
    function translateMock () {
        return 'Hello, FDConf!';
    }
    mockery.registerMock('translate', translateMock);
    mockery.enable();
 
    var hashTag = require('../src/hashTagGenerator.js');
    var actual = hashTag('Привет, FDConf!');
    actual.should.be.equal('#HelloFDConf');
});

Первый шаг

Пишите тесты сразу

Не убедил?

web application

nock


var nock = require('nock');

nock('https://translate.com')
    .get('/text=Привет+FDConf')
    .reply(200, 'Hello FDConf);
        

Тестирование - это:

приятно

удобно

рабочий код

удовольствие

уверенность

Спасибо!


speaker.should.deep.equal({
    face: My face!,
    name: 'Сергей',
    twitter: '@sergey_zhigalov',
    email: 'zhigalov@yandex-team.ru'
});