Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
Mocha 
Покрой свой фронт-енд по полной
О себе 
Владислав Безверхий, web-developer, A2 Design 
Интересуюсь IT с 2005 года 
Пишу всякие штуки для себя с 2009 года 
Работаю в IT с 2012 года 
Увлекаюсь музыкой, играювожу D&D. Люблю маму, папу, фракталы, 
парадоксы и функциональное программирование.
Жизнь без тестов 
● Нельзя с уверенностью сказать что код работает. 
● Каждая новая фича - беда для разработчика 
● Боишься регрессий как огня 
● При удачном стечении обстоятельств может сломаться production и 
разбиться сердце заказчика  product owner’а
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
Решение? 
● соблюдение принципов разработки (например 
KISS/DRY) 
● Модульное тестирование (Unit tests) 
● Функциональное тестирование 
● соблюдение code-styles 
● применение методологии разработки
А что с фронт-ендом? 
● Зависимость от сервера 
● Зависимость от браузера 
● DOM дерево - не всегда твой друг 
● Иногда бывает callback hell 
● Можно ловкой глобальной переменной 
сломать всё
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
Что из этого можно решить с 
помощью Unit-тестов? 
● Зависимость от сервера - mock объекты 
● Зависимость от браузера - можно прогонять тесты в 
разных браузерах. 
● Операции над DOM деревом можно тестировать 
● Сallback hell - с помощью spy функций 
● Глобальная переменная быстро проявит себя в 
хорошо написанных тестах
Средства тестирования 
Jasmine JS 
● полная поддержка BDD 
● methods chaining 
● написана в первую очередь для браузера 
● имеются пакеты для python и ruby 
● дружит с Sinon
Средства тестирования 
Mocha js 
● Поддерживает BDD и TDD 
● Написана с поддержкой браузера и Node JS 
● Возможность подключать разные библиотеки для asserts 
● Дружит с Sinon и Chai
Toolkit 
● Mocha 
● Chai 
● Sinon
Toolkit 
● Mocha - запускает тесты 
● Chai - предоставляет API 
● Sinon - mock объекты
Подключение 
> npm install mocha 
> npm install chai 
> npm install sinon
Подключаем в браузер (Mocha + Chai) 
<head> 
<title>Express Tests</title> 
<link rel="stylesheet" href="/stylesheets/tests/mocha. 
css"> 
<script src="/javascripts/mocha.js"></script> 
<script src="/javascripts/chai.js"></script> 
<script src=”/javascripts/chai.js”></script> 
</head>
Пишем первый тест 
var should = chai.expect; 
describe('Array', function() { 
describe('#indexOf()', function(){ 
it('should return -1 when the value is not present', function(){ 
should([1, 3, 4].indexOf(5)).equal(-1); 
should([1, 3, 4].indexOf(0)).equal(-1); 
}); 
}); 
});
Тестируем свой код 
var MapHandler = function() { 
var self = this; 
self.squareSide = 0.11; 
self.init(); 
}; 
MapHandler.prototype.coordsPolynome = function(x) { 
x = Math.abs(x); 
return 1.75477 * Math.pow(10, -6) * Math.pow(x, 3) -0.000365418 
* Math.pow(x, 2) + 0.00845663 * x + 0.954619;// 
};
Тестируем свой код 
describe('coordsPolynome', function() { 
it('should be clear', function() { 
assert.equal(MapHandler.coordsPolynome(50),MapHandler.coordsPolynome(50)); 
assert.equal(MapHandler.coordsPolynome(-10),MapHandler.coordsPolynome(10)); 
}); 
});
Запускаем тесты 
<script> 
mocha.setup('bdd'); 
mocha.bail(false); 
mocha.run(); 
</script> 
</body>
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
А что еще дает нам chai?
Chai plugins 
● Для фреймворков 
● Для jQuery 
● Для всевозможных изменений (DOM, 
promises etc.) 
● Можно писать свои - есть API
Немного о Sinon 
● Function Spies 
● Stubs 
● Fake Ajax 
● Fake XMLHttpRequest 
● Fake Server 
● Faking Time
Sinon - simplify your testing 
● Framework- agnostic (поддерживает 
Jasmine, Mocha) 
● Нет зависимостей 
● Можно использовать как на сервере так и 
в браузере
Sinon Spies 
Проблемы: 
● Отслеживание вызовов callback’ов 
● Отслеживание контекста внутри callback’ов 
● Отслеживание переменных переданных в callback 
● Отслеживание влияния callback’а на внешний scope
function once(fn) { 
var returnValue, called = false; 
return function () { 
if (!called) { 
called = true; 
returnValue = fn.apply(this, arguments); 
} 
return returnValue; 
}; 
}
it("calls the original function", function () { 
var callback = sinon.spy(); 
var proxy = once(callback); 
proxy(); 
assert(callback.called); 
});
it("calls the original function only once", function () { 
var callback = sinon.spy(); 
var proxy = once(callback); 
proxy(); 
proxy(); 
assert(callback.calledOnce); 
// ...or: 
// assert.equals(callback.callCount, 1); 
});
it("calls original function with right this and args", function() 
{ 
var callback = sinon.spy(); 
var proxy = once(callback); 
var obj = {}; 
proxy.call(obj, 1, 2, 3); 
assert(callback.calledOn(obj)); 
assert(callback.calledWith(1, 2, 3)); 
});
Sinon - Mocks 
● Тестируемая функция зависит от какого 
либо внешнего API (например 
фреймворка) 
● Тестируема функция зависит от внешнего 
объекта
it("returns the return value from the original function", 
function () { 
var myAPI = { method: function () {} }; 
var mock = sinon.mock(myAPI); 
mock.expects("method").once().returns(42); 
var proxy = once(myAPI.method); 
assert.equals(proxy(), 42); 
mock.verify(); 
});
Sinon - Stubs 
Проблемы: 
● Вызов функций за пределами теста 
● Дублирование тестов 
● Результат вызываемой функции зависит 
от временивнешних условий 
● Функция является замыканием
it("returns the return value from the original function", 
function () { 
var stub = sinon.stub().returns(42); 
var proxy = once(stub); 
assert.equals(proxy(), 42); 
});
Sinon - Fake XHR 
Проблемы: 
● Время отклика замедляет тестирование 
● Сервер может быть не всегда доступен 
● Запрос формируется динамически 
● Выполнение запроса повлечет изменение 
данных на сервере
var xhr, requests; 
before(function() { 
xhr = sinon.useFakeXMLHttpRequest(); 
requests = []; 
xhr.onCreate = function (req) { requests.push(req); }; 
}); 
after(function() { 
// Like before we must clean up when tampering with globals. 
xhr.restore(); 
});
it("makes a GET request for todo items", function () { 
getSomething(42, sinon.spy()); 
assert.equal(requests.length, 1); 
assert.equal(requests[0].url, "/something/42"); 
});
Sinon (Fake Server) - testing 
Проблемы: 
● Недоступностьотсутствие сервера 
● Время отклика 
● Несоответствие API сервера текущей 
спецификации 
● Запросы влияют на данные сервера
var server; 
beforeEach(function () { server = sinon.fakeServer.create(); }); 
afterEach(function () { server.restore(); });
it("returns ok", function() { 
var callback = sinon.spy(); 
getSomething(42, callback); 
// This is part of the FakeXMLHttpRequest API 
server.requests[0].respond( 
200, 
{ "Content-Type": "application/json" }, 
JSON.stringify([{ id: 1, text: "Provide examples", done: 
true }]) 
); 
assert.equal(200, server.requests[0].status); 
})
it("calls callback with deserialized data", function () { 
var callback = sinon.spy(); 
getTodos(42, callback); 
// This is part of the FakeXMLHttpRequest API 
server.requests[0].respond( 
200, 
{ "Content-Type": "application/json" }, 
JSON.stringify([{ id: 1, text: "Provide examples", done: 
true }]) 
); 
assert(callback.calledOnce); 
});
Sinon (Timers) - prepare 
Проблемы: 
● Может сильно замедлить прохождение 
тестов 
● Изменение состояния объектов в рамках 
setInterval 
● Очень не удобно отлаживать
function throttle(callback) { 
var timer; 
return function () { 
clearTimeout(timer); 
var args = [].slice.call(arguments); 
timer = setTimeout(function () { 
callback.apply(this, args); 
}, 100); 
}; 
}
var clock; 
before(function () { clock = sinon.useFakeTimers(); }); 
after(function () { clock.restore(); });
it("calls callback after 100ms", function () { 
var callback = sinon.spy(); 
var throttled = throttle(callback); 
throttled(); 
clock.tick(99); 
assert(callback.notCalled); 
clock.tick(1); 
assert(callback.calledOnce); 
assert.equal(new Date().getTime(), 100); 
}
Смотреть тесты фронтенда в 
консоли? 
> npm install mocha-phantomjs
Смотреть тесты фронтенда в 
консоли? 
<script> 
if (window.mochaPhantomJS) { mochaPhantomJS.run(); } 
else { mocha.run(); } 
</script>
Смотреть тесты фронтенда в 
консоли? 
<script> 
if (window.mochaPhantomJS) { mochaPhantomJS.run(); } 
else { mocha.run(); } 
</script> 
$ mocha-phantomjs -R dot /test/file.html 
$ mocha-phantomjs http://testserver.com/file.html 
> npm install mocha-phantomjs 
$ mocha-phantomjs -s localToRemoteUrlAccessEnabled=true -s webSecurityEnabled=false 
http://testserver.com/file.html 
$ mocha-phantomjs -p ~/bin/phantomjs /test/file.html
Спасибо за внимание 
@rabbiabram

More Related Content

2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной

  • 1. Mocha Покрой свой фронт-енд по полной
  • 2. О себе Владислав Безверхий, web-developer, A2 Design Интересуюсь IT с 2005 года Пишу всякие штуки для себя с 2009 года Работаю в IT с 2012 года Увлекаюсь музыкой, играювожу D&D. Люблю маму, папу, фракталы, парадоксы и функциональное программирование.
  • 3. Жизнь без тестов ● Нельзя с уверенностью сказать что код работает. ● Каждая новая фича - беда для разработчика ● Боишься регрессий как огня ● При удачном стечении обстоятельств может сломаться production и разбиться сердце заказчика product owner’а
  • 5. Решение? ● соблюдение принципов разработки (например KISS/DRY) ● Модульное тестирование (Unit tests) ● Функциональное тестирование ● соблюдение code-styles ● применение методологии разработки
  • 6. А что с фронт-ендом? ● Зависимость от сервера ● Зависимость от браузера ● DOM дерево - не всегда твой друг ● Иногда бывает callback hell ● Можно ловкой глобальной переменной сломать всё
  • 8. Что из этого можно решить с помощью Unit-тестов? ● Зависимость от сервера - mock объекты ● Зависимость от браузера - можно прогонять тесты в разных браузерах. ● Операции над DOM деревом можно тестировать ● Сallback hell - с помощью spy функций ● Глобальная переменная быстро проявит себя в хорошо написанных тестах
  • 9. Средства тестирования Jasmine JS ● полная поддержка BDD ● methods chaining ● написана в первую очередь для браузера ● имеются пакеты для python и ruby ● дружит с Sinon
  • 10. Средства тестирования Mocha js ● Поддерживает BDD и TDD ● Написана с поддержкой браузера и Node JS ● Возможность подключать разные библиотеки для asserts ● Дружит с Sinon и Chai
  • 11. Toolkit ● Mocha ● Chai ● Sinon
  • 12. Toolkit ● Mocha - запускает тесты ● Chai - предоставляет API ● Sinon - mock объекты
  • 13. Подключение > npm install mocha > npm install chai > npm install sinon
  • 14. Подключаем в браузер (Mocha + Chai) <head> <title>Express Tests</title> <link rel="stylesheet" href="/stylesheets/tests/mocha. css"> <script src="/javascripts/mocha.js"></script> <script src="/javascripts/chai.js"></script> <script src=”/javascripts/chai.js”></script> </head>
  • 15. Пишем первый тест var should = chai.expect; describe('Array', function() { describe('#indexOf()', function(){ it('should return -1 when the value is not present', function(){ should([1, 3, 4].indexOf(5)).equal(-1); should([1, 3, 4].indexOf(0)).equal(-1); }); }); });
  • 16. Тестируем свой код var MapHandler = function() { var self = this; self.squareSide = 0.11; self.init(); }; MapHandler.prototype.coordsPolynome = function(x) { x = Math.abs(x); return 1.75477 * Math.pow(10, -6) * Math.pow(x, 3) -0.000365418 * Math.pow(x, 2) + 0.00845663 * x + 0.954619;// };
  • 17. Тестируем свой код describe('coordsPolynome', function() { it('should be clear', function() { assert.equal(MapHandler.coordsPolynome(50),MapHandler.coordsPolynome(50)); assert.equal(MapHandler.coordsPolynome(-10),MapHandler.coordsPolynome(10)); }); });
  • 18. Запускаем тесты <script> mocha.setup('bdd'); mocha.bail(false); mocha.run(); </script> </body>
  • 20. А что еще дает нам chai?
  • 21. Chai plugins ● Для фреймворков ● Для jQuery ● Для всевозможных изменений (DOM, promises etc.) ● Можно писать свои - есть API
  • 22. Немного о Sinon ● Function Spies ● Stubs ● Fake Ajax ● Fake XMLHttpRequest ● Fake Server ● Faking Time
  • 23. Sinon - simplify your testing ● Framework- agnostic (поддерживает Jasmine, Mocha) ● Нет зависимостей ● Можно использовать как на сервере так и в браузере
  • 24. Sinon Spies Проблемы: ● Отслеживание вызовов callback’ов ● Отслеживание контекста внутри callback’ов ● Отслеживание переменных переданных в callback ● Отслеживание влияния callback’а на внешний scope
  • 25. function once(fn) { var returnValue, called = false; return function () { if (!called) { called = true; returnValue = fn.apply(this, arguments); } return returnValue; }; }
  • 26. it("calls the original function", function () { var callback = sinon.spy(); var proxy = once(callback); proxy(); assert(callback.called); });
  • 27. it("calls the original function only once", function () { var callback = sinon.spy(); var proxy = once(callback); proxy(); proxy(); assert(callback.calledOnce); // ...or: // assert.equals(callback.callCount, 1); });
  • 28. it("calls original function with right this and args", function() { var callback = sinon.spy(); var proxy = once(callback); var obj = {}; proxy.call(obj, 1, 2, 3); assert(callback.calledOn(obj)); assert(callback.calledWith(1, 2, 3)); });
  • 29. Sinon - Mocks ● Тестируемая функция зависит от какого либо внешнего API (например фреймворка) ● Тестируема функция зависит от внешнего объекта
  • 30. it("returns the return value from the original function", function () { var myAPI = { method: function () {} }; var mock = sinon.mock(myAPI); mock.expects("method").once().returns(42); var proxy = once(myAPI.method); assert.equals(proxy(), 42); mock.verify(); });
  • 31. Sinon - Stubs Проблемы: ● Вызов функций за пределами теста ● Дублирование тестов ● Результат вызываемой функции зависит от временивнешних условий ● Функция является замыканием
  • 32. it("returns the return value from the original function", function () { var stub = sinon.stub().returns(42); var proxy = once(stub); assert.equals(proxy(), 42); });
  • 33. Sinon - Fake XHR Проблемы: ● Время отклика замедляет тестирование ● Сервер может быть не всегда доступен ● Запрос формируется динамически ● Выполнение запроса повлечет изменение данных на сервере
  • 34. var xhr, requests; before(function() { xhr = sinon.useFakeXMLHttpRequest(); requests = []; xhr.onCreate = function (req) { requests.push(req); }; }); after(function() { // Like before we must clean up when tampering with globals. xhr.restore(); });
  • 35. it("makes a GET request for todo items", function () { getSomething(42, sinon.spy()); assert.equal(requests.length, 1); assert.equal(requests[0].url, "/something/42"); });
  • 36. Sinon (Fake Server) - testing Проблемы: ● Недоступностьотсутствие сервера ● Время отклика ● Несоответствие API сервера текущей спецификации ● Запросы влияют на данные сервера
  • 37. var server; beforeEach(function () { server = sinon.fakeServer.create(); }); afterEach(function () { server.restore(); });
  • 38. it("returns ok", function() { var callback = sinon.spy(); getSomething(42, callback); // This is part of the FakeXMLHttpRequest API server.requests[0].respond( 200, { "Content-Type": "application/json" }, JSON.stringify([{ id: 1, text: "Provide examples", done: true }]) ); assert.equal(200, server.requests[0].status); })
  • 39. it("calls callback with deserialized data", function () { var callback = sinon.spy(); getTodos(42, callback); // This is part of the FakeXMLHttpRequest API server.requests[0].respond( 200, { "Content-Type": "application/json" }, JSON.stringify([{ id: 1, text: "Provide examples", done: true }]) ); assert(callback.calledOnce); });
  • 40. Sinon (Timers) - prepare Проблемы: ● Может сильно замедлить прохождение тестов ● Изменение состояния объектов в рамках setInterval ● Очень не удобно отлаживать
  • 41. function throttle(callback) { var timer; return function () { clearTimeout(timer); var args = [].slice.call(arguments); timer = setTimeout(function () { callback.apply(this, args); }, 100); }; }
  • 42. var clock; before(function () { clock = sinon.useFakeTimers(); }); after(function () { clock.restore(); });
  • 43. it("calls callback after 100ms", function () { var callback = sinon.spy(); var throttled = throttle(callback); throttled(); clock.tick(99); assert(callback.notCalled); clock.tick(1); assert(callback.calledOnce); assert.equal(new Date().getTime(), 100); }
  • 44. Смотреть тесты фронтенда в консоли? > npm install mocha-phantomjs
  • 45. Смотреть тесты фронтенда в консоли? <script> if (window.mochaPhantomJS) { mochaPhantomJS.run(); } else { mocha.run(); } </script>
  • 46. Смотреть тесты фронтенда в консоли? <script> if (window.mochaPhantomJS) { mochaPhantomJS.run(); } else { mocha.run(); } </script> $ mocha-phantomjs -R dot /test/file.html $ mocha-phantomjs http://testserver.com/file.html > npm install mocha-phantomjs $ mocha-phantomjs -s localToRemoteUrlAccessEnabled=true -s webSecurityEnabled=false http://testserver.com/file.html $ mocha-phantomjs -p ~/bin/phantomjs /test/file.html