Test.it — Не Снова, А Снова

Добрый день, хабр.

После моего статьи О Попробуй это Это была неделя вечности.

И как бы я ни планировал продлить этот срок хотя бы на месяц, пришло время новой публикации.

Картинка для привлечения внимания:

test.it — не снова, а снова

С тех пор библиотека (во многом благодаря хабравитам) приобрел новый функционал.

И в связи с тем, что синтаксис, приведенный в предыдущей статье, не полностью работает в текущей версии, я не имею права откладывать эту статью еще на 3 недели.

Кто не любит много слов - Веб-сайт где вы можете увидеть код в действии, GitHub , Вики Появилась цепочка звонков

  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
   

test.it(some) .

comment('comment to test') .

callback(function(){alert('test has been passed')}) .

arguments(); // -> [some] test.group('group', function(){ .

}) .

comment('comment to group') .

result(); // -> true/false

и новый механизм гнездования

test.group('first group',function(){ .

test.group('second group', function(){ .

}); .

}); test.group('first group').

group('second group',function(){ .

});

Новый способ отображения ошибок

test.it — не снова, а снова

И два дополнительных метода test.typeof() И тест.trace() .

А еще 3 Вики -страницы.



А теперь обо всем этом более подробно.

Так.

Давайте посмотрим на пример, приведенный в вики : Пока мы еще не приступили к тестированию, давайте воспользуемся методом test.typeof() .



console.log( test.typeof(1) ,test.typeof("text") ,test.typeof([1,2,3]) ,test.typeof({a:1,b:2}) ,test.typeof() ,test.typeof(document) ,test.typeof(document.getElementsByTagName("body")) ,test.typeof(window) ,test.typeof(/yes it is RegExp/) // and many many more .

);

test.typeof() — определяет тип передаваемого ему значения.

Он может различать: Множество , логическое значение , Дата , Ошибка ( Эвалеррор , ДиапазонОшибка , Ошибка ссылки , Ошибка синтаксиса , ТипОшибка , УриОррор ), Функция , НЭН И Число , Объект , Регэксп , Нить , Окно , HTML , список узлов .

И он также определит пустую переменную как 'неопределенный' но в отличие от стандартного тип не сможет получить необъявленную переменную в качестве аргумента.

Он ответит за это 'неопределенный' к несуществующему свойству объявленного и непустого объекта.

Но это уже специфика языка.

Если мы обновим страницу, в консоли появится следующая строка:

test.it — не снова, а снова

Теперь давайте посмотрим на метод тест.trace() .



(function firstFunction() { (function secondFunction() { (function lastFunction() { console.log(test.trace()); })(); })(); })();

тест.trace() - возвращает список (собраны в строки, разделенные «\n») строки кода, которые были выполнены для вызова этого метода.

На самом деле это не реально след() (чего, к сожалению, нет в JavaScript) , потому что из него были вырезаны все ссылки на вызовы внутри библиотеки.

В вывод консоли теперь будет добавлено следующее:

test.it — не снова, а снова

Здесь и далее несущественные части вывода консоли на скриншотах будут опущены, чтобы бессмысленно не увеличивать размер изображений.



Приступим к тестированию.

Для начала создадим объект для тестов, переменную с целочисленным значением и пустую переменную.



var Family = { name: "Desiderio", pet: { type: "dog", name: "google" }, members: [ { name: "Titulus", age: 23 }, { name: "Dude", age: Infinity } ] } var myIQ = 100; var Nothing;

Думаю, вопросов по этому коду быть не должно.

Давайте двигаться дальше.

Следующий шаг – первое испытание.



Тест на нефальшь.

Здесь все так же, как и раньше.



test.it("hello world"); test.done();

test.it(значение) — создает новый тест, проверяя ценить к неложности.

тест.done() — завершает тесты и выводит результат на консоль.

В дальнейшем будет считаться, что тест.done() появляется в последней строке нашего кода.

Я буду опускать это в примерах.

В консоли видим:

test.it — не снова, а снова

Где:

  • корень — имя группы нулевого уровня.

  • pass — статус группы, означающий, что все тесты/группы в ней пройдены успешно.

  • 1/0/0 — количество пройденных/неудачных/ошибочных тестов/групп соответственно.

  • (9 мс) — время в миллисекундах, потраченное на тесты.

Если развернуть эту группу, то можно увидеть в ней список тестов/групп.



test.it — не снова, а снова

Прежде чем мы рассмотрим наш единственный тест, давайте разберем и его:

test.it — не снова, а снова

Так:

  • pass — статус теста, означающий, что он пройден
  • аргумент не является ложным — описание типа теста и его результата
  • ["Привет, мир"] — массив аргументов, передаваемых в тест


Попробуем использовать механизм цепочность .

Самый простой случай — добавление комментария:

test.it(2+2==5) .

comment("i badly taught algebra at school");

.

комментарий(текст) — добавляет комментарий к тесту/группе, в цепочке которого он был вызван.

При этом продолжая цепочку, но об этом чуть позже.

Этот код также можно записать так:

test.it(2+2==5).

comment("i badly taught algebra at school");

Но, для случаев с более длинными цепочками, удобнее и понятнее писать столбиком.

Сейчас корень (расширяется автоматически, поскольку содержит неудавшийся тест) выглядит следующим образом:

test.it — не снова, а снова

В счетчиках в первой строке можно заметить увеличение второй цифры от 0 до 1, что означает увеличение количества неудачных тестов/групп.

Обратим внимание на тот, который открыт по умолчанию.

(потому что он не был принят) , тест. От предыдущего он отличается только статусом «пройден», что означает, что тест провален, и комментарием «Я плохо преподавала алгебру в школе», который мы добавили.

Очевидно, что в test.it(значение) Можно передавать не только строки, но и переменные, выражения, вызовы функций и т. д. В принципе, что бы мы ни передавали, оно сначала будет выполнено, будет получен результат, и этот результат пойдет в тест. Именно так устроен JavaScript. Давайте проверим то, что мы только что сказали.

Давайте проверим выражение:

test.it(Infinity>Infinity-1) .

comment("philosophically is not it?");

Об этом выражении можно подумать за чашкой кофе, мы здесь ради чего-то другого.

Результат теста похож на результат предыдущего, что очевидно.



test.it — не снова, а снова

Пока мы не уйдем в дебри цепных звонков, и мы обязательно уйдем, Давайте посмотрим на другой вариант теста Попробуй это() .



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

Давайте сравним ранее объявленную переменную с другим значением.



test.it(myIQ,"genious") .

comment("is I'm genious?"); test.it(myIQ,(1+10)*12 - 34 + 5*5*5 - 123) .

comment("check my IQ to be a normal");

test.it(значение1, значение2) — проверяет равенство двух переданных ему значений.

В консоли эти 2 теста будут выглядеть так:

test.it — не снова, а снова

Ничего необычного, но стоит обратить внимание на описание первого (неудачного) теста.

" аргументы имеют разные типы » — в этом тексте содержится подсказка, объясняющая нам, почему тест не удался — переданные аргументы разного типа.

Теперь попробуем более сложные цепочки.

Давайте выполним некоторые действия в зависимости от результата теста.



if (test.it(Family) .

comment("Is Family exist? Is it not empty?") .

result()) { console.info("by if: ","Yep! Here it is!"); } else { console.warn("by if: ","ALARM! there are no Family"); }

.

результат() — завершает цепочку и возвращает результат теста.

В этом коде мы проверяем Семья на нефальшь и в зависимости от результата выводим в консоль разные фразы.

Вывод консоли теперь выглядит так:

test.it — не снова, а снова

Правда, такую задачу предпочтительнее выполнять с помощью другого цепного вызова:

test.it(Nothing) .

comment("Is Nothing exist? Is it not empty?") .

callback( function(){console.info("by callback: ","Yep! Here it is!");} ,function(){console.warn("by callback: ","ALARM! there are no Nothing");});

.

callback( function(){ /* funcIfpass */}[, function(){ /* funcIffall */}[, function(){ /* funcIferror */}]]) — выполняет одну из трёх функций в зависимости от результата прохождения теста или группы.

При этом продолжая цепочку.

В результате в консоли видим:

test.it — не снова, а снова

Такая конструкция предпочтительнее, поскольку не завершает цепочку.



Группы

Теперь давайте создадим первую группу.



test.group("Empty group",function(){});

test.group( имя, функция() {… } ) - создает новую группу или обращается к существующему и заполняет его тестами, расположенными в функции, переданной в качестве второго аргумента.

Но поскольку никаких тестов не было сдано, группа создается пустая, но со статусом пройдено - потому что в ней нет ни одного непройденного теста.



test.it — не снова, а снова

Прежде чем сделать скриншот, я развернул группу — это видно по поворачивающейся вниз стрелке и двум серым пикселям, указывающим ее конец.

Но поскольку группа пуста, похоже, что она закрытая.

Хорошо.

Перейдем к более осмысленным действиям.

Давайте создадим группу с двумя тестами внутри и комментарием.



test.group('Family tests',function(){ test.it(Family.name,"Zukerberg") .

comment("Are we test Zukerberg's family?"); test.it(Family.name,"Desiderio") .

comment("Or Desiderio's?"); }).

comment("unite!");

Видите – ничего сложного!

test.it — не снова, а снова

Здесь вам просто нужно обратить внимание на комментарий, который мы добавили объединяйтесь! - в шапке группы.

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

И не просто тесты, а тесты, инициирующие новые тесты в зависимости от их результата.

Добавим следующий код:

test.group("Family tests",function(){ test.it(Family.pet) .

comment("Do we have a pet?") .

callback(function(){ // I add test in your test, so you can test while you testing test.it(Family.pet,{type:"dog", name:"google"}) .

comment("Is it right pet?"); }); test.it(Family.house) .

comment("Do we have a House?") .

callback(function(){ // next test will not be executed test.it(Family.pet,{type:"Huge", color:"green"}) .

comment("Is it right House?"); }); });

Принимая во внимание последние 2 теста в этой группе и еще 4 только что описанных теста, всего их будет 5 (sic!).

Проверить это можно на калькуляторе.



test.it — не снова, а снова

Вы видите это в названии? 3 прошли, 2 не прошли - всего 5.

Новые тесты

Пришло время взглянуть на парочку необычных тестов.

Для начала в группе «А вот и странные тесты» Давайте создадим следующие два теста:

test.them([Family.pet, Family.members]) .

comment("There must be memebers with pet, to call it a 'Family'"); test.types([Family.pet.name, Family.name],"string") .

comment("Is names are string type");

test.them(значения) - аналог test.it(значение) , только в качестве первого аргумента он принимает массив, в котором уже проверяет элементы на неложность.

test.types(значения [, тип]) - а также test.them(значения) проверяет элементы массива, переданные в качестве первого аргумента.

Но он проверяет их на соответствие типа, и если передан второй аргумент тип - затем берет этот тип за образец.

У этого теста есть упрощенный аналог, но об этом чуть позже.

Вот как они выглядят в консоли:

test.it — не снова, а снова

Я для ясности открыл тесты вместе с их массивами аргументов, но мне почему-то кажется, что ясность от этого только уменьшилась.

И вот вам еще одна цепочка магии:

var numberOfMembers = test.type(Family.members,"Array") .

comment("Is it a several members, nor a single member?") .

arguments()[0].

length; test.it(numberOfMembers>5) .

comment("Is it big family?");

.

аргументы() — завершает цепочку вызовов и возвращает переданные в тест аргументы ( не в группе! ).



test.it — не снова, а снова

Поясню - первый тест, проверил значение Семья.

члены не ложь.

Поскольку это массив из двух элементов, тест пройден.

аргументы()[0] == Семья.

члены .

Поэтому в переменную количество членов вводится количество элементов массива Семья.

члены то есть 2. Второй тест не пройден из-за того, что 2 не больше 5.

Вложение

Ты еще помнишь, что мы в группе» вот и странные тесты "? Добавим сюда еще одну группу и сразу воспользуемся конструкцией для для создания нескольких однотипных тестов.



test.group("Members age",function(){ for (i=0;i<numberOfMembers;i++) { test.it(Family.members[i].

age>25) .

comment("Is "+Family.members[i].

name+" older then 25?"); } });



test.it — не снова, а снова

Теперь эта новая группа" Возраст участников «расположен в старом» вот и странные тесты ".



Ошибки

Давайте добавим в одну группу" Возраст участников «Еще один тест:

test.it() .

comment("yep, here is error");

Этот код приведет к ошибке, потому что Попробуй это() ожидает получить от 1 до 2 аргументов.



test.it — не снова, а снова

В заголовке ошибки:

  • RangeError — тип ошибки
  • ожидается хотя бы один аргумент — описание, помогающее понять причину его возникновения.

Затем приходит результат тест.trace() чтобы было легче найти его в коде.

И сам объект ошибки в данном случае ДиапазонОшибка - это если кто-то хочет вникнуть поглубже.



Ссылки на группы

Вернёмся на уровень корень .

На всякий случай напомню, что группа " вот и странные тесты "сейчас выглядит так:

test.it — не снова, а снова

В нем есть еще одна группа» Возраст участников «.

Теперь мы добавим в него тест.

test.group("here comes strange tests").

group("Members age",function(){ test.it("bye") .

comment("good"); });

test.group(имя) — возвращает ссылку на группу, после чего ее можно использовать как начало цепочки, для добавления новой группы тестов или добавления тестов в существующую подгруппу.

Это последнее, что мы только что сделали.

Теперь в консоли мы видим:

test.it — не снова, а снова

И напоследок, чтобы закрепить все вышесказанное: полный список со всеми выводами консоли

console.log( // look how do test.typeof() work test.typeof(1) ,test.typeof("text") ,test.typeof([1,2,3]) ,test.typeof({a:1,b:2}) ,test.typeof() ,test.typeof(document) ,test.typeof(document.getElementsByTagName("body")) ,test.typeof(window) ,test.typeof(/yes it is RegExp/)); (function firstFunction() { // look how do test.trace() work (function secondFunction() { (function lastFunction() { console.log(test.trace()); })(); })(); })(); var Family = { // Here some complex object name: "Desiderio", pet: { type: "dog", name: "google" }, members: [ { name: "Titulus", age: 23 }, { name: "Dude", age: Infinity } ] } var myIQ = 100; // and value var Nothing; // and empty value test.it("hello world"); // Let"s add some simple tests test.it(2+2==5).

comment("i badly taught algebra at school"); // with comment test.it(Infinity>Infinity-1).

comment("philosophically is not it?"); // with expression // check equalence test.it(myIQ,"genious").

comment("is I'm genious?"); test.it(myIQ,(1+10)*12 - 34 + 5*5*5 - 123).

comment("check my IQ to be a normal"); // try some chain staff if (test.it(Family).

comment("Is Family exist? Is it not empty?").

result()) { console.info("by if: ","Yep! Here it is!"); } else { console.warn("by if: ","ALARM! there are no Family"); } // do it again in better way test.it(Nothing).

comment("Is Nothing exist? Is it not empty?").

callback( function(){console.info("by callback: ","Yep! Here it is!");} ,function(){console.warn("by callback: ","ALARM! there are no Nothing");}); test.group("Empty group",function(){}); // try to make a group test.group('Family tests',function(){ // let's unite it! test.it(Family.name,"Zukerberg").

comment("Are we test Zukerberg's family?"); test.it(Family.name,"Desiderio").

comment("Or Desiderio's?"); }).

comment("unite!"); test.group("Family tests",function(){ // and add some test after test.it(Family.pet).

comment("Do we have a pet?") .

callback(function(){ // I add test in your test, so you can test while you testing test.it(Family.pet,{type:"dog", name:"google"}).

comment("Is it right pet?"); }); test.it(Family.house).

comment("Do we have a House?") .

callback(function(){ // next test will not be executed test.it(Family.pet,{type:"Huge", color:"green"}).

comment("Is it right House?"); }); }); test.group("here comes strange tests",function(){ // test existance of most important Family properties test.them([Family.pet, Family.members]) .

comment("There must be memebers with pet, to call it a 'Family'"); // test types of names test.types([Family.pet.name, Family.name],"string") .

comment("Is names are string type"); // here some magic var numberOfMembers = test.type(Family.members,"Array") .

comment("Is it a several members, nor a single member?") .

arguments()[0].

length; test.it(numberOfMembers>5).

comment("Is it big family?"); // So if we know how many members there, lets check their age test.group("Members age",function(){ for (i=0;i<numberOfMembers;i++) { test.it(Family.members[i].

age>25) .

comment("Is "+Family.members[i].

name+" older then 25?"); } test.it().

comment("yep, here is error"); // add some error to see the trace }); }); // add final test deep in group test.group("here comes strange tests").

group("Members age",function(){ test.it("bye").

comment("good"); }); test.done();



test.it — не снова, а снова



корень

О, да.

test.root до сих пор лежит на своем месте.

Его также можно использовать для создания новых вариантов отображения результатов.

Он был немного упрощен (счетчики групп больше не разделяют группы и тесты).

Пустой корень выглядит так:

{ "type": "group", "name": "root", "time": 0, "result": { "pass": 0, "fail": 0, "error": 0, "total": 0 }, "stack": [] }



Заключение

Мне бы очень хотелось поблагодарить: Недостатки, перечисленные в предыдущей статье, остались.

Но уже есть очень интересные мысли о методах их решения.

Веб-сайт где вы можете увидеть весь приведенный выше код в действии, GitHub , Вики Теги: #JavaScript #tdd #тестирование #открытый исходный код #JavaScript #tdd

Вместе с данным постом часто просматривают: