2010-05-05 4 views
8

Во многих структурах, внутренние переменные функции используются в качестве частных переменных, напримерДоступ к внутренней функции переменных в Javascript

Raphael = (function(){ 
    var _private = function(a,b) {return a+b;}; 
    var _public = function(a) {return _private(a,a);} 
    var object = {mult2:_public}; 
    return object; 
})(); 

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

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

Мне нужно провести тестирование некоторых объектов Raphael использует внутренне (в приведенном выше примере я хочу выполнить модульные тесты на объекте private). Как я могу проверить их?

Редактировать: Я получил комментарии об модульных тестах, которые должны тестировать публичные интерфейсы.

Позвольте мне указать прецедент. Я пишу библиотеку под названием Raphael. Предполагается, что эта библиотека добавляет только одно имя в глобальное пространство имен и не более того. Это своеобразное требование для Javascript, поскольку Javascript не имеет пространств имен.

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

require 'linked_list' 
Raphael = (function(){/* use linked list */})(); 

Однако Javascript не позволяет мне сделать это в любом случае, что бы не загрязнять глобальный масштаб с связанным объектом списка! Я поэтому обязан встраивать linked_list в локальной области видимости Рафаэля:

Raphael = (function(){ 
    /* implement linked list */ 
    var linked_list = function(){/*implementation*/}; 
})(); 

А теперь я хочу проверить linked_list реализацию.

+3

идея unittests заключается в проверке только открытых функций/методов – Andrey

+0

@Andrey, Javascript - это не ваш обычный язык! Вы не можете импортировать другие модули разумным способом, поэтому, если я включу небольшую, скажем, связанную реализацию списка для Raphael, ее нужно будет встроить во внутреннюю функцию Рафаэля, чтобы она не подвергалась воздействию пользователя Рафаэля. Я не могу просто «включить» его ... –

+1

См. Http://stackoverflow.com/questions/2230469/javascript-sandbox-unit-testing http://stackoverflow.com/questions/716207/testing-private -functions-in-javascript http://stackoverflow.com/questions/1881078/testing-javascript-functions-inside-anonymous-functions et all – gnarf

ответ

0
var Raphael; 
var test = true; //or false; 

Raphael = (function(){ 
    var private = function(a,b) {return a+b;}; 
    var public = function(a) {return private(a,a);} 
    var object = {mult2:public}; 

    if (test) Raphael.private = private; 

    return object; 
})(); 
+0

И как я могу отменить «тестовый» код, когда я доставляю библиотеку пользователям , '# ifdef' ;-) –

+0

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

2

Попробуйте это:

var adder = function(a,b) { 
    return a + b; 
} 

Raphael = function(fn){ 
    var _private = function(a,b) { 
     fn(a,b); 
    } 

    var _public = function(a) { 
     return _private(a,a); 
    } 

    var object = {doubleIt: _public}; 

    return object; 
}(adder); 

Просто небольшая функция впрыска

+1

Вся идея иметь 'private' как var в теле функции не должна загрязнять глобальную область. В вашем решении «Рафаэль» загрязняет глобальную область с помощью «сумматора». –

+0

Если вы собираетесь идти по пути использования «частных свойств», вы должны либо иметь чувствительную переменную/функцию для проверки в частной собственности. или вы должны придерживаться общедоступного интерфейса. Также вы можете добавить еще один слой пространства имен в свой код и сделать его частным, а не там, где он находится. Единичный тест нижнего уровня, затем выполните интеграционный тест в API нового уровня. – Gutzofter

12

Вы по-прежнему отсутствует точка.

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

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

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

var Raphael= function(listIterator) { 
    listIterator.method(); 
}(new ListIterator()); 

Ложные хаки, такие как показано ниже, совершенно неуместны (в модульных тестах или в любом месте).

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

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

Относительно кода, который вы отправили, вы забыли var, пропустили точку с запятой и использовали два зарезервированных слова в качестве идентификаторов: private и public.

Последствия использования var - это возможность запускать ошибки и проблемы, связанные с различными реализациями нестандартных объектов типа «GlobalScopePolluter» («Объект не поддерживает это свойство или метод», видимый в IE). Последствием использования FutureReservedWord является SyntaxError. Реализация может обеспечить расширение синтаксиса до , позволяющее FutureReservedWord как идентификатор, и действительно многие делают, однако лучше не полагаться на такие расширения, и если вы получили ошибку, это будет полностью ваша ошибка.

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

// DO NOT USE THIS CODE. 
var Raphael = (function(){ 
    var _private = function(a,b) {return a+b;}; 
    var _public = function(a) {return _private(a,a);}; 
    var object = {mult2:_public}; 
    return object; 
})(); 

var leakedFunction; 

// Spurious hack: 
// Give valueOf a side effect of leaking function. 
// valueOf is called by the _private function as a 
// side effect of primitive conversion, where 
// ToPrimitive(input argument, hint Number) results 
// in calling valueOf. 

function valueOfSnoop(){ 
    leakedFunction = leakedFunction || valueOfSnoop.caller || function(){}; 
    return 2; 
} 

var a = { 
    valueOf : valueOfSnoop 
}; 

Raphael.mult2(a, 3); 
var privateMathod = leakedFunction; 
alert(leakedFunction(1, 2)); 

Этот пример кода является лишь демонстрацией того, что такая вещь возможна. Учитывая выбор, он является плохой альтернативой альтернативам, упомянутым ранее; либо измените свой дизайн, либо измените свои тесты.

+0

@Garrett, любая причина для объявления функции() {} вместо просто {}. Мне это нравится! обычно при объявлении 'var' мне нравится кодировать тип только для защитного кодирования. – Gutzofter

+0

Если вы имеете в виду var leakedFunction = function(){}; - я делаю это, потому что leakedFunction называется позже. – Garrett

+0

Но только после того, как вы переназначили его с помощью этого 'arguments.callee.caller'. Да? – Gutzofter

1

Лучшим решение, которое я придумал:

В исходном использовании Javascript файла

Raphael = (function(){ 
// start linked_list 
    var linked_list = function() {/*...*/}; 
// end linked_list 
    var object = {mult2:_public}; 
    return object; 
})(); 

Теперь, используя скрипт для извлечения объектов между // start ([a-zA-Z_]*) и // end ([a-zA-Z_]*) и модульным тестированием извлеченного кода.

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

Смежные вопросы