2015-12-17 4 views
1

Я запрограммировал очень простой генератор в Python 3.4:Тестирование генератора в Python

class Gen: 
    def __init__(self, xml_lines, attribs): 
    #... 

    def _get_values(self, xml_line): 
    # get only specific "attribs" from a line in a "xml" 

    def values(self): 
    for line in self.xml_lines: 
     yield self._get_values(line) 

код работает, когда я использую для цикла, чтобы потреблять сгенерированные значения:

reader = Gen(...): 
for v in reader.values(): 
    print(v) 

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

import unittest 
#... 
reader = Gen(...): 
v = reader.values() 
self.assertIsNone(v) 

Когда я пытаюсь это, я всегда получаю

AssertionError: <generator object next_values at 0x7fe2a5f9e3f0> is not None 

Так что, когда я называю значения себя, что-то вернуть (это указатель ?) вместо генерируемого значения.

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

+2

Если вы хотите экземпляры вашего класса чтобы быть последовательным, вы должны внедрять '__iter__' и' __next__', см., например, https://docs.python.org/3/library/stdtypes.html#typeiter. Кроме того, у вас есть проблема, что 'next_value()' создает совершенно новый генератор, не давая вам следующего значения из существующего (поэтому имя мало вводит в заблуждение). – jonrsharpe

+0

@jonrsharpe Получил это! Я меняю имя метода, чтобы отразить ваше важное наблюдение ... Я получал неправильный метод. Благодарю. Но на самом деле мне не нужны методы '__iter__' и' __next__', поскольку «yield» дает мне все сразу. – Nigini

+0

Вам не нужно '__next__' обязательно, но если вы переименуете его метод в' __iter__', вы можете перебрать его непосредственно по экземпляру, а не явно вызвать метод для этого. – jonrsharpe

ответ

3

Генератор предназначен для повторения. Вы можете использовать функцию next(), чтобы получить следующее значение генератора. Имейте в виду, что если ваш генератор исчерпал свои значения, он поднимет исключение StopIteration.

reader = Gen(...): 
values = reader.values() 
v = next(values) 
self.assertIsNone(v) 
+0

Хотя это будет работать, я подозреваю, что OP надеется на реализацию, так что 'next (reader)' будет генерировать 'None'. – jonrsharpe

+0

О, мужчина! Я думал, что пробовал это. В любом случае, спасибо @Brendan. Что для меня странно, так это то, что я не мог найти никакой информации о «объекте » ... даже не был «dir (v)»! Можете ли вы поделиться своим источником? – Nigini

+1

@Nigini [Здесь [(https://docs.python.org/2/reference/datamodel.html) - я не могу напрямую ссылаться на точку на странице, а искать страницу «функции генератора». Они всегда возвращают объект итератора. Вы можете вызвать '.next()' на этом объекте, чтобы получить следующее значение (или использовать встроенную функцию 'next()' на нем). Возможно, это одна из вещей, которые могут быть лучше документированы в документах python. –

0

мне нужно модульного тестирования генератора себя, поэтому я создал эту вспомогательную функцию:

def generator_tester(generator_iterator_to_test, expected_values): 
    range_index = 0 
    for actual in generator_iterator_to_test: 
     assert range_index + 1 <= len(expected_values), 'Too many values returned from range' 
     assert expected_values[range_index] == actual 
     range_index += 1 

    assert range_index == len(expected_values, 'Too few values returned from range' 

Вот пример его использования:

generator_tester(
    Gen(...), 
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 
) 
Смежные вопросы