Sinon fake timer. Интеграционные тесты. Supertest. Тестирование в браузере
Жигалов Сергей
... Сделать из твитов бегущую строку. Для этого нужно выводить на консоль по одному символу раз в 100ms.
setTimeout
setTimeout(() => {
    console.log('Прошла секунда');
}, 1000);
        console.log
console.log('Несколько');
console.log('строк');
        
Несколько
строк
        process.stdout.write
process.stdout.write('Одна');
process.stdout.write('строка');
        
Однастрока
        
// crawline.js
function crawline(text, cb) {
    const letters = text.split('');
    function print() {
        if (!letters.length) return cb();
        process.stdout.write(letters.shift());
        setTimeout(print, 100);
    }
    print();
}
        
// tests/crawline-test.js
const crawline = require('../crawline');
describe('Crawline', () => {
    it('shoult ptint text', done => {
        crawline('I don’t always bend time and ' +
            'space in unit tests, but when I do, ' +
            'I use Buster.JS + Sinon.JS', done);
    });
});
        npm test 
    npm test -- --timeout=30000 
    
beforeEach(() => clock = sinon.useFakeTimers());
afterEach(() => {
    clock.restore();
    process.stdout.write.restore();
});
        
it('shoult ptint text', done => {
    const write = sinon.spy(process.stdout, 'write');
    crawline('I don’t always bend time and ' +
        'space in unit tests, but when I do, ' +
        'I use Buster.JS + Sinon.JS', () => {
            assert.equal(write.callCount, 91);
            done();
        });
    clock.tick(10000);
});
        
    Crawline
I don’t always bend time and space in unit tests,
but when I do, I use Buster.JS + Sinon.JS
    ✓ shoult ptint text
    1 passing (32ms)
        
// tests/formatDate-test.js
const sinon = require('sinon');
describe('Format date', () => {
    let clock;
    before(function () {
        const startDate = new Date(2017, 3, 25).getTime();
        clock = sinon.useFakeTimers(startDate);
    });
    after(() => clock.restore());
    /* ... */
});
        
it('should return only time', function () {
    var actual = formatDate(new Date(2017, 3, 25, 6, 17, 6));
    expect(actual).to.equal('06:17');
});
it('should return `вчера` with time', function () {
    var actual = formatDate(new Date(2017, 3, 24, 8, 17, 6));
    expect(actual).to.equal('вчера в 08:17');
});
        тестирование группы взаимодействующих модулей.
 
    
// poker.js
const playPoker = require('./playPoker');
function poker(firstDice, secondDice) {
    try {
        const result = playPoker(firstDice, secondDice);
        console.log(result);
    } catch(error) {
        console.error(error.message);
    }
}
        
// tests/poker-test.js
it('should print success result', () => {
    const log = sinon.stub(console, 'log');
    const error = sinon.stub(console, 'error');
    const playPoker = sinon.stub();
    playPoker.withArgs([1, 2, 3, 4, 5], [1, 2, 3, 4, 6]).returns('Ничья');
    const poker = proxyquire('../poker', { './playPoker': playPoker });
    poker([1, 2, 3, 4, 5], [1, 2, 3, 4, 6]);
    assert(log.calledOnce);
    assert(log.calledWith('Ничья'));
    assert(!error.called);
});
        
 function weather(cb) {
     request(url, (requestError, res, body) => {
         if (requestError || res.statusCode !== 200) {
             return cb('Request error');
         }
         try {
             const data = JSON.parse(body);
             cb(null, data.fact.temp);
         } catch (parseError) {
             cb(parseError.message);
         }
     });
 }
         
// tests/weather-test.js
it('should print temperature', done => {
    nock('https://api.weather.yandex.ru')
        .get('/v1/forecast')
        .reply(200, '{"fact":{"temp":25}}');
    weather((error, actual) => {
        assert.equal(actual, 25);
        done(error);
    });
});
        
// tests/weather-test.js
it('should print temperature', done => {
    weather((error, actual) => {
        assert(Number.isInteger(actual));
        done(error);
    });
});
        Написать универсальный конвертер величин.
            npm install convert-units --save
        
const convert = require('convert-units')
convert(1.5)
    .from('week')
    .to('min'); // 15120
        express
npm install express --save
        
const express = require('express');
const app = express();
app.get('/convert', function (req, res) {
    res.json({ result: 1 });
});
app.listen(3000);
        
const express = require('express');
const convert = require('convert-units')
const app = express();
app.get('/convert', (req, res) => {
    const result = convert(req.query.value)
        .from(req.query.from)
        .to(req.query.to);
    res.json({ result });
});
app.listen(3000);
         
    supertest
npm install supertest --save-dev
        
const express = require('express');
const convert = require('convert-units')
const app = express();
app.get('/convert', (req, res) => {
    const result = convert(req.query.value)
        .from(req.query.from)
        .to(req.query.to);
    res.json({ result });
});
app.listen(3000);
        
// app.js
const express = require('express');
const convert = require('convert-units')
const app = express();
app.get('/convert', (req, res) => {
    const result = convert(req.query.value)
        .from(req.query.from)
        .to(req.query.to);
    res.json({ result });
});
module.exports = app;
        
// index.js
require('./app').listen(3000);
        
// tests/convertor-test.js
const request = require('supertest');
const app = require('../app');
describe('Convertor controller', () => {
    it('should return result', () => {
        /* ... */
    });
});
        
it('should return result', () => {
    return request(app)
        .get('/convert')
        .query({ value: 1.5, from: 'week', to: 'min' })
        .expect(200)
        .expect('Content-Type', /json/)
        .expect({ result: 15120 });
});
        Реализовать веб-версию универсального конвертера величин.
// app.js
const express = require('express');
const convert = require('convert-units');
const app = express();
app.use(express.static('public'));
app.get('/convert', (req, res) => {
    const result = convert(req.query.value)
        .from(req.query.from)
        .to(req.query.to);
    res.json({ result });
});
app.listen(3000);
        
<head>
    <!-- подключаем стили, чтобы тесты вяглядели красиво -->
    <link href="path/to/mocha.css" rel="stylesheet" />
</head>
        
<body>
    <!-- относительно этого элемента
         выводится тестовый отчет -->
    <div id="mocha"></div>
    <!-- конфигурируем и запускаем тесты -->
    <script src="path/to/mocha.js"></script>
    <script>mocha.setup('bdd')</script>
    <script src="../tests/convertor-test.js"></script>
    <script>mocha.run();</script>
</body>
        
"./node_modules/mocha/mocha.css"
"./node_modules/mocha/mocha.js"
        
"https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.css"
"https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.js"
        
<script src="http://chaijs.com/chai.js"></script>
        
<script>
    chai.assert.equal(1 + 1, 2);
</script>
        
chai.expect(1 + 1).to.equal(2);
chai.expect(Boolean(1)).to.be.true;
        
chai.should();
[1, 2, 3].should.deep.equal([1, 2, 3]);
[1, 2, 3].should.have.length(3);
[1, 2, 3].should.be.an('array');
        
describe('Convertor controls', () => {
    it('should enable `convert` button for correct value', () => {
        const send = document.getElementById('send');
        chai.assert.equal(send.getAttribute('disabled'), null);
    });
});
        
describe('Convertor controls', () => {
    it('should disable `convert` button for invalid value', () => {
        const from = document.getElementById('from');
        const send = document.getElementById('send');
        from.value = 'invalid value';
        from.dispatchEvent(new Event('input'));
        chai.assert.equal(send.getAttribute('disabled'), 'disabled');
    });
});
        phantomjsPhantomJS is a headless WebKit scriptable with a JavaScript API.
mocha-phantomjs
npm install mocha-phantomjs --save-dev
node_modules/.bin/mocha-phantomjs
    -p node_modules/.bin/phantomjs
    https://urfu-2016-convertor.herokuapp.com/convert-test.html
Convertor controls
  ✓ should enable `convert` button for correct value
  ✓ should disable `convert` button for invalid value
2 passing (8ms)
        Spectacular Test Runner for JavaScript
npm install karma --save-dev
        
npm install karma-mocha karma-chai --save-dev
        
npm install karma-chrome-launcher --save-dev
        
node_modules/.bin/karma init karma.config.js
         
    
// convertor.js
const value = document.getElementById('value');
const from = document.getElementById('from');
const to = document.getElementById('to');
const send = document.getElementById('send');
const result = document.getElementById('result');
function isValid() { /* ... */ }
function change() { /* ... */ }
function convert()  { /* ... */ }
        
describe('Convertor', function () {
    beforeEach(function() {
        var fixture = `
            
                 from
                 to
                
                
                
            
        `;
        document.body.insertAdjacentHTML('afterbegin', fixture);
    });
});
        
it('should enable `convert` button for correct value', function() {
    const send = document.getElementById('send');
    chai.assert.equal(send.getAttribute('disabled'), null);
});
it('should disable `convert` button for invalid value', function() {
    const from = document.getElementById('from');
    const send = document.getElementById('send');
    from.value = 'invalid unit';
    from.dispatchEvent(new Event('input'));
    chai.assert.equal(send.getAttribute('disabled'), 'disabled');
});