2015-02-18 2 views
0

Я часто слышал, как разработчики говорят, что тестовый код должен быть «уродливым» и простым, насколько это возможно.Реализация логики в модульных тестах, чтобы сделать синтаксис более коротким и тестовый код более повторно использоваться

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

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

Вопрос в том, являются ли действующими модульными тестами?

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

Как сделать код более модульным (AMD? Browsify?), Без необходимости реализовывать все с нуля или внедрять фреймворк.

В настоящее время глобальное состояние изменено во многих файлах, и много закрытий мне не нравится. Как я могу сделать код в закрытии еще проверяемым? this package возможно?

Пример кода:

utils.js

function getRealTemplatValues (inputs, templateFn, outerId, innerClass) { 
    var i, res; 
    for (i = 0; i < inputs.length; i++) { 
     res = templateFn(inputs[i]); 
     $('#' + outerId).append(res); 
    } 
    return $('.' + innerClass).map(function(){ 
     return $(this).html(); 
    }).get(); 
}; 


function assertEqual (done, expect, inputs, outputs, fn, decorator) { 
    function iter (input, output, cb) { 
     var i; 
     for (i = 0; i < inputs.length; i++) { 
      cb(input[i], output[i]); 
     } 
    }; 

    function cb (input, output) { 
     output = !!decorator ? decorator(output) : output; 
     expect(fn(input, decorator)).toBe(output); 
     done && done(); 
    } 

    iter(inputs, outputs, cb, decorator); 
}; 

helper.scenario.js

describe('helpers', function() { 
    var inputs = ["#string#string#string", "string#string", "string#string#string#", "#", "###"], 
     outputs = ["#string #string #string", "string #string", "string #string #string#", "#", "###"], 
     decorString = 'magic!', 
     outerId = "container", 
     innerClass = "inner", 
     decorator = function(input) {return input + decorString}; 


    describe('breakHashtags - unit', function(done) { 
     it('should break hashtags with prefixed with spaces - non decorated', function (done) { 
      return assertEqual(done, expect, inputs, outputs, breakHashtags); 
     }); 

     it('should break hashtags with prefixed with spaces - decorated', function() { 
      return assertEqual(done, expect, inputs, outputs, breakHashtags, decorator); 
     }); 
    }); 

    describe('handle bars integration', function() { 
     var outerId = "container", 
      outerClass = "inner", 
      fixture = '<div id="' + outerId + '"></div>', 
      template = Handlebars.compile('<div class="' + outerClass + '">{{breakHashtags hashtags}}</div>'), 
      decoratedTemplate = Handlebars.compile('<div id="inner">{{breakHashtags hashtags decoratorHelper}}</div>'); 
     beforeEach(function() { 
      addFixture(fixture); 
     }); 
     afterEach(function() { 
      clearMyFixtures(); 
      Handlebars.helpers['decoratorHelper'] && delete Handlebars.helpers['decoratorHelper']; 
     }); 

     it('should have the breakHashtags function registered as a helper', function() { 
      expect(Handlebars.helpers['breakHashtags']).toEqual(breakHashtags); 
     }); 

     it('should replace hashtags with hashtags prefixed with spaces', function(){ 
      var realValues = getRealTemplatValues(inputs, template, outerId, outerClass); 
      assertEqual(done, expect, inputs, realValues, breakHashtags); 
     }); 

     it('should replace hashtags with hashtags prefixed with ' + 
      'spaces and invoke the decorator on theo put', function(){ 
      Handlebars.registerHelper('decoratorHelper', decorator); 
      var realValues = getRealTemplatValues(inputs, decoratedTemplate, outerId, outerClass); 
      assertEqual(done, expect, inputs, realValues, breakHashtags, decorator); 
     }); 
    }); 

}); 

helpers.js:

function breakHashtags (text, decorator) { 
    var pattern = /\w(#).+/i, 
     p1, p2, idx = text.search(pattern), prefix = ''; 
    while (idx > 0) { 
     if (idx === 1) { 
      text = text.substring(idx); 
      prefix = text.substring(0, 1); 
     } 
     else{ 
      p1 = text.substring(0, idx + 1); 
      p2 = text.substring(idx + 1); 
      text = p1 + ' ' + p2; 
      console.log(p1, p2, text) 
     } 

     idx = text.search(pattern); 
    } 
    return !!decorator ? decorator(prefix + text) : prefix + text; 

} 

Handlebars.registerHelper('breakHashtags', breakHashtags); 

ответ

2

мой мнение: Я думаю, вы пошли слишком далеко. например:

it('should replace hashtags with hashtags prefixed with spaces', function(){ 
      var realValues = getRealTemplatValues(inputs, template, outerId, outerClass); 
      assertEqual(done, expect, inputs, realValues, breakHashtags); 
}); 

оно может быть более читаемым для вас, но только для вас. программирование - это командная игра. когда кто-то еще видит этот код в первый раз, он понятия не имеет, что происходит. В этом тесте есть описание, но он не имеет ничего общего с тем, что вы на самом деле проверяете. не знаю точно, что делает ваш код, но тесты должны выглядеть примерно так:

it('should replace hashtags with hashtags prefixed with spaces', function(){ 
      var result = testedFunction("#hashtag1#hashtag2#hashtag3"); 
      assertThat(result).isEqualTo("#hashTag1 #hashTag2 #hashTag3"); 
}); 

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

Как я могу сделать код более модульным (AMD? Browserify?), Не имея реализовать все с нуля или ввести структуру.

что с фреймворком? ведь вы только начали писать свои собственные. лучше использовать стороннюю структуру, потому что все в вашей команде знают, что именно она делает. когда вы пишете своих собственных помощников, каждый должен их изучать с нуля. Кроме того, лучше написать помощников, которые делают ваши тесты более мощными в общем. не стесняйтесь создавать простые рамки для параметризованного тестирования или утверждения общего назначения. совершенно ясно, как это работает, что он делает, когда и как его следует изменить.поэтому вы можете использовать их во всех своих тестах

но когда вы создаете такие функции, как assertEqual (done, expect, inputs, outputs, fn, decorator), тогда никто не знает, что он делает. почему у ада assertEquals есть так много параметров? он должен иметь не более 2-3 (фактическое, ожидаемое, сообщение об ошибке). так что я могу изменить этот assertEquals, когда мне нужно? или я должен оставить его как есть, потому что это важно для кого-то и просто скопировать-вставить его? такие испытания на техническое обслуживание кошмар

Я часто слышал, разработчики говорят, что тестовый код должен быть «некрасиво», и ясно, как это возможно.

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

+0

Благодарим вас за вход сэр. Я создам помощник жасмина, который создает тестовый пример для списка входов против списка выходов, если он еще не существует. –

+0

да, это хорошая идея. и это относительно простая задача - несколько строк кода – piotrek

+0

Кстати, вы действительно должны указать атрибуты DDO 'priority' и' terminal' в своей статье с угловыми вложенными директивами в своем блоге –