У меня есть такой код:
class A():
def __init__(self, a):
self.a = a
def outer_method(self):
def inner_method():
return self.a +1
return inner_method()
Я хочу написать тест для inner_method. Для этого я использую такой код:
def find_nested_func(parent, child_name):
"""
Return the function named <child_name> that is defined inside
a <parent> function
Returns None if nonexistent
"""
consts = parent.__code__.co_consts
item = list(filter(lambda x:isinstance(x, CodeType) and x.co_name==child_name, consts ))[0]
return FunctionType(item, globals())
Вызов его с помощью find_nested_func(A().outer_method, 'inner_method'), но он не работает при вызове «FunctionType», потому что функция не может быть создана, поскольку «self.a» перестает существовать в тот момент, когда функция перестает быть внутренней функцией. Я знаю, что конструкция FunctionType может принимать в качестве аргумента замыкание, которое может решить эту проблему, но я не знаю, как его использовать. Как я могу пройти это?
Ошибка, которую он дает, следующая:
return FunctionType(item, globals())
TypeError: arg 5 (closure) must be tuple






Почему вы пытаетесь проверить inner_method? В большинстве случаев вам следует тестировать только части общедоступного API. outer_method является частью общедоступного API A, поэтому протестируйте именно его. inner_method — это деталь реализации, которая может измениться: что, если вы решите переименовать ее? что, если вы немного рефакторите его, не изменяя внешне видимое поведение outer_method? У пользователей класса A нет (простого) способа вызова inner_method. Модульные тесты обычно предназначены только для проверки того, что могут вызывать пользователи вашего класса (я предполагаю, что они предназначены для модульных тестов, потому что интеграционные тесты с такой гранулярностью были бы странными - и тот же принцип в основном будет соблюдаться).
На практике у вас возникнут проблемы с извлечением функций, определенных в области действия другой функции, по нескольким причинам, включая захват переменных. У вас нет возможности узнать, захватывает ли inner_method только self или outer_method выполняет некоторую логику и вычисляет некоторые переменные, которые использует inner_method. Например:
class A:
def outer_method():
b = 1
def inner_method():
return self.a + b
return inner_method()
Кроме того, у вас могут быть управляющие операторы вокруг определения функции, поэтому нет возможности решить, какое определение используется, не запуская outer_method. Например:
import random
class A:
def outer_method():
if random.random() < 0.5:
def inner_method():
return self.a + 1
else:
def inner_method():
return self.a + 2
return inner_method()
Вы не можете извлечь inner_method здесь, потому что их два, и вы не знаете, какой из них на самом деле используется, пока не запустите outer_method.
Так что просто не тестируйте inner_method.
Если inner_method действительно достаточно сложен, чтобы вы хотели протестировать его изолированно (и если вы это сделаете, принципиальное тестирование говорит, что вы должны имитировать его использование, например, его использование в outer_method), то просто сделайте его «приватным» методом. на A:
class A:
def _inner_method(self):
return self.a + 1
def outer_method(self):
return self._inner_method()
Принципиальное тестирование говорит, что вам действительно не следует тестировать методы подчеркивания, но иногда этого требует необходимость. Такой способ позволяет вам тестировать _inner_method так же, как и любой другой метод. Затем при тестировании outer_method вы можете смоделировать его, выполнив a._inner_method = Mock() (где a — это тестируемый A объект).
Также используйте class A. Скобки не нужны, если у вас нет родительских классов.
Для таких случаев я бы рекомендовал последний подход в конце моего ответа. Как я объяснил выше, извлечение внутреннего метода во время выполнения в некоторых случаях будет сложным, если не невозможным.
Я не могу сделать что-то вроде: def _inner_method(self): return self.a + 1 Потому что метод не может получить ни одного аргумента (ни самого себя), потому что он будет передан в библиотеку, которая не готова использовать методы, принимающие аргументы.
Я не понимаю вашего комментария, но self не является аргументом. Это неявно. Когда вы делаете A()._inner_method(), вам не нужно проходить self. Если вы хотите передать _inner_method кому-то, вы можете просто сделать foo = A()._inner_method. foo связан с self, поэтому пользователь этого может просто сделать foo(). Кроме того, как библиотека, которая использует inner_method, получает это в первую очередь, если это внутренний метод? Как я описал выше, это сложно для начала. Наконец, если inner_method используется библиотекой, сделайте его общедоступным методом (удалите ведущий _) и протестируйте его отдельно.
Я понимаю вашу точку зрения. Я хочу выполнить тест для внутренней функции, потому что операции, которые она выполняет, довольно сложны, поэтому было бы неплохо иметь возможность легко определить, происходит ли ошибка во внутренней или внешней функции. И он должен быть внутренним, а не частным, потому что это может вызвать проблемы при выполнении некоторых операций из-за определенных библиотек, которые он использует.