2008-11-28 2 views
8

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

def outer(): 
    def inner(): 
     pass 

outer.inner() 

что приводит к сообщению об ошибке:

AttributeError: 'function' object has no attribute 'inner'

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

ответ

5

Соглашение Python должно называть «частные» функции и методы с лидирующим подчеркиванием. Когда вы видите лидирующий знак подчеркивания, вы не должны пытаться его использовать.

Python is not Java.

+11

Как это ответить на вопрос? Существуют ли хорошие решения для изоляции таких внутренних функций для модульного тестирования или нет? Даже без доступа к внутреннему из внешнего функционального объекта такие, казалось бы, могут быть достигнуты с использованием парсера модулей python, ast или токенизатора, чтобы копить сам код. Очевидно, это легче сказать, чем кодировать. :-) – 2011-07-09 16:02:06

+1

Он отвечает на вопрос, потому что один не проверяет * частные детали реализации. Вы можете полагаться на инструменты тестирования покрытия, чтобы сказать вам, если вы проверите публичный интерфейс, адекватно реализуйте частную реализацию. – SingleNegationElimination 2011-07-09 16:24:50

11

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

Обратите внимание, что внутренняя функция не является простой функцией, это закрытие. Рассмотрим этот случай:

def outer(a): 
    b = compute_something_from(a) 
    def inner(): 
     do_something_with(a, b) 

Это стандартная возможность проверки соответствия. Если ваш cyclomatic complexity слишком высок, ваши тесты будут слишком многочисленными.

1

Я не думаю, что есть шанс получить доступ к inner() из пространства имен extern.

Однако, на мой взгляд, тот факт, что вы храните внутреннее() вложенное, подразумевает, что единственным «контрактом», который действительно имеет значение, является внешний(). inner() является частью реализации, и вы не должны пытаться протестировать реализацию. Если вы действительно хотите протестировать inner(), выполните обширные тесты на external() с данными, которые будут включать в себя все функции inner().

2

Невозможно получить внутреннюю функцию из внешнего объекта функции (см. Другие ответы!). Тем не менее, как модульные тесты, так и закрытие сделали (по крайней мере, для меня) удивительные улучшения производительности разработчиков. Можем ли мы оба? Можем ли мы изолировать изолированные вложенные функции?

Не легко.

Однако такое может быть достигнуто с использованием парсера модулей python, ast или токенизатора, чтобы вырезать сам код, извлекая внутренние функции (через некоторый путь через вложенность) и позволяя тестировать их с состоянием из (значения для закрытых имен) и stubs/mocks для более вложенных функций (определенных в тестовой цели).

Кто-нибудь знает что-нибудь подобное? Google не смог найти ничего.

1

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

def outer(): 
    def inner(): 
     pass 

    if __debug__: 
     test_inner(inner) 
     # return 

def test_inner(f): 
    f() # this calls the inner function 

outer() 

В принципе, вы можете отправить внутреннюю функцию как параметр снаружи и протестировать ее по своему усмотрению.При вызове external() ваш тест будет запущен, а поскольку это замыкание, он сохранит любое дополнительное свойство из внешней функции (например, переменных). Используя список, вы можете отправить столько функций, сколько пожелаете. Для того, чтобы игнорировать если, вариант, это запустить подобный код:

python -O code.py 
1

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

Примеры вложенных функций:

def f(v1): 
    v2 = 1 
    def g(v3=2): 
    return v1 + v2 + v3 + 4 
    def h(): 
    return 16 
    return g() + h() + 32 

class C(object): 
    def foo(self): 
    def k(x): 
     return [ self, x ] 
    return k(3) 

def m(): 
    vm = 1 
    def n(an=2): 
    vn = 4 
    def o(ao=8): 
     vo = 16 
     return vm + an + vn + ao + vo 
    return o() 
    return n() 

Они могут быть протестированы с помощью такого рода кода:

import unittest 
from nested import nested 

class TestNested(unittest.TestCase): 
    def runTest(self): 
    nestedG = nested(f, 'g', v1=8, v2=1) 
    self.assertEqual(nestedG(2), 15) 
    nestedH = nested(f, 'h') 
    self.assertEqual(nestedH(), 16) 
    nestedK = nested(C.foo, 'k', self='mock') 
    self.assertEqual(nestedK(5), [ 'mock', 5 ]) 
    nestedN = nested(m, 'n', vm=1) 
    nestedO = nested(nestedN, 'o', vm=1, an=2, vn=4) 
    self.assertEqual(nestedO(8), 31) 

def main(argv): 
    unittest.main() 

if __name__ == '__main__': 
    import sys 
    sys.exit(main(sys.argv)) 

Небольшой модуль помощник nested выглядит следующим образом:

import types 

def freeVar(val): 
    def nested(): 
    return val 
    return nested.__closure__[0] 

def nested(outer, innerName, **freeVars): 
    if isinstance(outer, (types.FunctionType, types.MethodType)): 
    outer = outer.func_code 
    for const in outer.co_consts: 
    if isinstance(const, types.CodeType) and const.co_name == innerName: 
     return types.FunctionType(const, globals(), None, None, tuple(
      freeVar(freeVars[name]) for name in const.co_freevars)) 
Смежные вопросы