#python #pytest
Вопрос:
Большинство примеров, которые я видел, показывают простой случай, когда входы/выходы могут быть выражены в строке:
@pytest.mark.parametrize("test_input", [1, 2, 3, 4])
Как мне следует работать с параметрами, требующими одной или двух строк изменений?
экс:
test_input1 = User()
test_input1.name = 'John'
test_input2 = User()
test_input2.phone = '1234567890'
test_input2.address = '123 Main St'
test_input3 = User()
test_input3.initials.middle = 'A'
test_input4 = User()
test_input4.make_super_user()
Давайте предположим, что я не могу передать их в качестве параметров конструктора, так User(name='John')
что это не вариант.
Комментарии:
1. Сделать заводскую функцию, которая это делает? Напр..
def create_user(**attrs): user = User(); for attr, value in attrs.items(): setattr(user, attr, value); return user
Ответ №1:
Есть несколько способов сделать это:
- Предоставьте информацию, необходимую для настройки объектов, затем выполните настройку в тестовой функции. Это немного громоздко для ваших конкретных примеров, когда настройка выполняется не по обычному шаблону, но все равно работает:
@pytest.mark.parametrize( 'params', [ dict( name='John', ), dict( phone='1234567890', address='123 Main St', ), dict( initials=dict(middle='A'), ), dict( is_super_user=True, ), ] ) def test_user(params): user = User() if 'name' in params: user.name = params['name'] if 'phone' in params: user.phone = params['phone'] if 'address' in params: user.phone = params['address'] if 'initials' in params: user.initials.middle = params['initials']['middle'] if params.get('is_super_user') user.make_super_user() assert ...
- Параметризуйте тестовую функцию с помощью заводских функций. Эта идея немного отличается от того, что предлагал Джонршарп в комментариях, потому что в этом случае вы бы написали отдельную фабрику для каждого тестового случая. Тем не менее, вы могли бы сократить шаблон для примеров 1 и 2, написав «фабричную фабрику», используя код, аналогичный коду Джонршарпа:
def make_user_with_name(): user = User() user.name = 'John' return user def make_user_with_phone_address(): user = User() user.phone = '1234567890' user.address = '123 Main St' return user def make_user_with_middle_initial(): user = User() user.initial.middle = 'A' return user def make_super_user(): user = User() user.make_super_user() return user @pytest.mark.parametrize( 'factory', [ make_user_with_name, make_user_with_phone_address, make_user_with_middle_initial, make_super_user, ] ) def test_user(factory): user = factory() assert ...
- Используется
exec()
для создания объектов из строк. Это хороший подход, когда вы загружаете параметры из отдельного файла (что я настоятельно рекомендую; см. parametrize_from_file), хотя в приведенном ниже примере для простоты есть все на python. Обратите внимание, что каждый фрагмент должен определять глобальную переменную с именемuser
:@pytest.mark.parametrize( 'snippet', [ """ user = User() user.name = 'John' """, """ user = User() user.phone = '1234567890' user.address = '123 Main St' """, """ user = User() user.initials.middle = 'A' """, """ user = User() user.make_super_user() """, ] ) def test_user(snippet): scope = {} exec(snippet, scope) user = scope['user'] assert ...
Комментарии:
1. exec выглядит привлекательно, помимо предоставления параметров в виде строк. parametrize_from_file выглядит как нечто, что мог бы перенять pytest.