Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Node.js > nodeunit, unit testing в JS


Автор: bilbobagginz 5.4.2013, 15:03
привет.
Вопрос немного философский конечно, но у кого есть мысли по сабжу ... буду рад услышать.
Тема такая - тестирование.
Пишется код (client side) на JS. пишется в форме модуля. 
Ессно, это не реально код приложения, а пример поясняющий проблему.
Если бы не было тестов, было бы грубо говоря так, mymodule.js:
Код

var mymodule = (function () {
    var that = {};
    var private_method = function (s) {
        if ( "undefined" === typeof s || "" === s ) {
           return undefined;
        }
        return "Hello, " + s + "!";
    };
    that.public_method = function (s) {
        return private_method(s);
    };
    return that;
})();

запуск этого кода - после добавки в файл HTML:
Код

// где-то в блоке script
mymodule.public_method("kuku");

Надо "покрыть" тестами (не слишком, но покрыть).
проблема в том, что я не нашел "архитектурно чистый" подход к тестированию приватных методов модуля.
с помощью nodeunit, я могу тестировать "public_method", создав каталог "test", и в нем файлик test_mymodule.js:
Код

var mymodule = require ('../mymodule.js');
exports.mymodule = {};
exports.mymodule.test_public = function (test) {
    test.equal(mymodule.public_method('kuku'), 'Hello, kuku!');
    test.done();
};

запуск теста просто:
Код

cd js # каталог, в котором скрипт и каталог test
nodeunit test/

Проблема в том, что доступа до mymodule.private_method() нету.
В данном примере, ессно, публичная метода - просто обертка и ни хрена не делает.
Но в реальном коде приватные методы - как раз то, что хотелось бы протестить.
И внешний API тоже, есссно. В общем идеи были разные.
Две наиболее популярные были:
1. "загрязнить" боевой модуль дополнительным тестовым кодом
Он потом отошлется клиенту непонятно к чему), приведея его к форме:
Код

var UNIT_TEST = ('UNIT_TEST' === process.env.NODE_ENV);
var mymodule = (function () {
    var that = {};
    var private_method = function (s) {
        if ( "undefined" === typeof s || "" === s ) {
           return undefined;
        }
        return "Hello, " + s + "!";
    };
    if (UNIT_TEST) { that.export.private_method = private_method; }
    that.public_method = function (s) {
        return private_method(s);
    };
    return that;
})();
if (UNIT_TEST) {
  module.exports.mymodule = mymodule;
} else {
  module.exports = mymodule;
}

в тесте определить переменную:
Код

process.env.NODE_ENV = 'UNIT_TEST';
var mymodule = require ('../mymodule.js');
exports.mymodule = {};
exports.mymodule.test_public = function (test) {
    test.equal(mymodule.mymodule.public_method('kuku'), 'Hello, kuku!');
    test.done();
};
exports.mymodule.test_public = function (test) {
    test.equal(mymodule.private_method('kuku'), 'Hello, kuku!');
    test.done();
};


2. разделить mymodule на внутренний под-модуль и тестить оный извне
т.е.:

Код

var UNIT_TEST = ('UNIT_TEST' === process.env.NODE_ENV);
var mymodule_util = (function () {
    var that = {};
    that.private_method = function (s) {
        if ( "undefined" === typeof s || "" === s ) {
           return undefined;
        }
        return "Hello, " + s + "!";
    };
    return that;
})();

var mymodule = (function () {
    var that = {};
    that.public_method = function (s) {
        return mymodule_util.private_method(s);
    };
    return that;
})();

if (UNIT_TEST) {
    module.exports.mymodule = mymodule;
    module.exports.mymodule_util = mymodule_util;
}


И соотв. в тесте будет 2 региона тестирования:
Код

process.env.NODE_ENV = 'UNIT_TEST';
var mymodule = require ('../mymodule.js');
exports.mymodule = {};
exports.mymodule.test_public = function (test) {
    test.equal(mymodule.mymodule.public_method('kuku'), 'Hello, kuku!');
    test.done();
};
exports.mymodule.test_public = function (test) {
    test.equal(mymodule.mymodule_util.private_method('kuku'), 'Hello, kuku!');
    test.done();
};


в C++ можно было сделать какой-то изврат с #ifdef, и этот изврат не попал бы в релизный бинарник.
Как же быть в JS?

Автор: CruorVult 5.4.2013, 16:39
Цитата(bilbobagginz @  5.4.2013,  15:03 Найти цитируемый пост)
проблема в том, что я не нашел "архитектурно чистый" подход к тестированию приватных методов модуля.


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

К тому же скоро готовится к релизу ECMAScript 6, где есть поддержка модулей. Там-то приватные методы вообще не получится протестировать. 

Автор: bilbobagginz 6.4.2013, 10:56
CruorVult, спасибо.
т.е. второй вариант (если много приватных метод вынести их в "утиль"ный модуль, использовать в главном, и тестить утильный модуль отдельно)

насчет "скоро".... 
гугл тут свой дротик (в смысле Dart) замутили... и сидят они как стандартные члены борда ECMA.
у тебя есть какая-то секретная инфа насчет когда это произойдет ?

Автор: CruorVult 8.4.2013, 11:09
Цитата(bilbobagginz @  6.4.2013,  10:56 Найти цитируемый пост)
т.е. второй вариант (если много приватных метод вынести их в "утиль"ный модуль, использовать в главном, и тестить утильный модуль отдельно)


Ничего никуда выносить не нужно. 
Тестировать приватные методы нужно, но неявно. По средствам паблик методов. 

Изначально ты все правильно сделал.

Код

test.equal(mymodule.public_method('kuku'), 'Hello, kuku!');
 

Осталось протестировать, когда метод ничего не принимает

Код

test.equal(mymodule.public_method(), undefined);
 

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

Лучше всего, при разработке с написанием тестов, использовать подход http://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B0_%D1%87%D0%B5%D1%80%D0%B5%D0%B7_%D1%82%D0%B5%D1%81%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5. Эта техника по началу кажется непонятной и запутанной, но позволяет очень глубоко обдумать архитектуру приложения без строчки рабочего кода.

 

Цитата(bilbobagginz @  6.4.2013,  10:56 Найти цитируемый пост)
у тебя есть какая-то секретная инфа насчет когда это произойдет ?


Точной информации нету, но предположительно к концу года.

Автор: bilbobagginz 9.4.2013, 21:12
Цитата(CruorVult @  8.4.2013,  10:09 Найти цитируемый пост)
Точной информации нету, но предположительно к концу года.

т.е. к концу года браузеры волшебным образом запоют на новом жаргоне ? smile (не верю)

Добавлено через 2 минуты и 34 секунды
Цитата(CruorVult @  8.4.2013,  10:09 Найти цитируемый пост)
Лучше всего, при разработке с написанием тестов, использовать подход TDD. Эта техника по началу кажется непонятной и запутанной, но позволяет очень глубоко обдумать архитектуру приложения без строчки рабочего кода.

знаком, но по физиологическим причинам страдаю излишним покрытием ненужного тестами, что нередко приводит к непокрытию нужноого smile
не знаю как лечиться smile

Автор: CruorVult 10.4.2013, 10:03
Цитата(bilbobagginz @  9.4.2013,  21:12 Найти цитируемый пост)
т.е. к концу года браузеры волшебным образом запоют на новом жаргоне ?  (не верю)


На фоне того, что Google переводит Chrome на новый движок рендеринга (Ripple) а также того, что Mozilla и Samsung разрабатывают новый движок для браузеров(под Android), переход на ECMAScript 6 меня ни капельки не удивляет  smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)