LINUX.ORG.RU
ФорумTalks

ненависть: python - тесты курильщика

 , ,


0

2

@gaga, Вы просили о тестах, их есть у нас. Как выглядит тест на нормальном языке, пример:

use Test::More;
plan tests => 6;

require_ok 'User';
my $user = new User name => 'Вася';
ok $user => 'Пользователь инстанцирован';
ok !$user->id, 'У нового пользователя id отсутствует';
ok $user->save, 'Пользователь сохранён';
ok $user->id, 'Id назначен при сохранении';
is $user->foo, 'bla', 'Значение по умолчанию для foo';

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

Теперь тест курильщика, переписываем то же самое.

import pytest
import importlib

# без переменных уровня модуля или подобных хаков данные
# из теста в тест не передать
User = None
vuser = None

def test_import():
   User = importlib.import_module('User')
   assert User, 'Модуль загружен'

def test_constructor():
   vuser = User(name='Вася')
   assert vuser, 'Пользователь инстанцирован'

def test_id_presave():
   assert not vuser.id, 'У нового пользователя id отсутствует'

def test_save():
   assert vuser.save(), 'Пользователь сохранён'

def test_id_postsave():
   assert vuser.id, 'Id назначен при сохранении'

def test_default_foo():
   assert vuser.foo == 'bla', 'Значение по умолчанию для foo'

Итого:

  • тестовая система (самая распространенная - pytest) сделана так, чтобы пользователь писал максимально много букв не относящиеся к тестам
  • передача данных из теста в тест затруднена
  • лог тестирования страшенный не человеко- и не машино- читаемый
  • вторая по популярности (или первая?) тестовая система еще более многосложна, предполагает не только объявления функций, но и классов
  • со стороны тестовой системы отсутствует поддержка частых паттернов: например тестирование строк на регекспы (like/unlike, тестирование импортируемости, итп)
  • тестирование количества тестов так же крайне неочевидно
  • автоматический подсчет статистики числа пройденных тестов в большой системе - так же ППЦ, ввиду отсутствия машиночитаемости
  • масса типовых слов, которые зарезервированы как ключевые (str, list, is, in, from, и так далее) заставляет изобретать сложные имена переменным

и тут еще мы не рассматриваем например ситуацию, что в начале теста система должна поднять некоторую инфраструктуру.

соответственно именно поэтому в питонячьих проектах тесты народ и не пишет обычно.

а еще эта хрень-кака молча глотает двойные


def foo():
   bla

def foo():
   ble

и вот где бы выбросить-то исключение? но нет же. Зачем? Надо же усложнить пользователю процесс поиска ошибки, а не упростить его!

пусть разбирается со страшенными стектрейсами, там где мог бы сразу получить точку с ошибкой, а так же пусть думает «почему код не выполняется?» там где мог бы получить ексепшен.

ППЦ

PPS: а еще есть уродские декораторы. какой шизик их придумал? причины почему их придумали понятны: за отсутствием нормальных лямбд хоть какой-то заменитель

★★

передача данных из теста в тест затруднена

Классы.

со стороны тестовой системы отсутствует поддержка частых паттернов: например тестирование строк на регекспы (like/unlike, тестирование импортируемости, итп)

А оно типо надо? Вот тебе import re и делай чехошь.

масса типовых слов, которые зарезервированы как ключевые (str, list, is, in, from, и так далее) заставляет изобретать сложные имена переменным

Проблемы с английским или с креативностью?

и тут еще мы не рассматриваем например ситуацию, что в начале теста система должна поднять некоторую инфраструктуру.

gtfy

И энивей от экономии пары символов перл так и не стал хоть сколько-то нужным.

creazero
()

Люблю читать нытьё нубиков-неосиляторов. Продолжай.

InterVi ★★★★
()

причины почему их придумали понятны: за отсутствием нормальных лямбд хоть какой-то заменитель

Какая связь между декораторами и лямбдами?

RazrFalcon ★★★★★
()

Только вчера натыкался в каком-то проекте на tests.py в виде плоского списка assert'ов как ты хочешь. Только пацанов не парило всякое говно в виде подсчета количества тестов и прочих свистоперделок. Хочешь свистоперделки - pytest или nose.

ei-grad ★★★★★
()
Ответ на: комментарий от creazero

Классы

покажи, как правильно передать юзера из теста в тест в данном случае используя классы

А оно типо надо? Вот тебе import re и делай чехошь.

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

rsync ★★
() автор топика

Погоди. Ты написал на перле 6 тестов для одного конструктора? Ну ок, а что тебе мешает на питоне запихнуть все assert'ы в одну функцию, т.к. все они тестируют один долбаный конструктор?

И кстати, ты случайно не о unittest мечтаешь?

https://habr.com/post/121162/

$ nosetests * -v --with-doctest
test_run_once (run_once.Test) ... ok
Doctest: run_once2.run_once ... ok
run_once3.test_run_once ... ok

---
Ran 3 tests in 0.008s

OK
router ★★★★★
()
Ответ на: комментарий от ei-grad

Только пацанов не парило всякое говно в виде подсчета количества тестов и прочих свистоперделок. Хочешь свистоперделки - pytest или nose.

ни питест ни ноз не считают количество тестов в виде теста.

rsync ★★
() автор топика

Очевидно же, что первый пример это какой-то arcane shit, а второе - читаемый и понятный код. Перл видел когда шел мимо кладбища, пистон просто не знаю.

ptarh ★★★★★
()
Ответ на: комментарий от router

а что тебе мешает на питоне запихнуть все assert'ы в одну функцию

assert - это исключение.

если один тест не проходит, все прочие (следующие за ним) не выполняются

rsync ★★
() автор топика
Ответ на: комментарий от router

здесь нет верификации числа тестов в тестах

допустим тестируем асинхронную систему:

  • запускаем http-сервер
  • запускаем несколько асинхронных запросов к нему
  • в обработчиках - вставляем тесты

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

для решения этой проблемы (в питоне понятное дело никто не задумывался - зачем?) и придумали тесты планировать.

заметь, придумали это даже не в Perl'е, а сперва в Java.

rsync ★★
() автор топика

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

Но твой редактор или линтер это подстветит https://imgur.com/a/Wgis85H

pawnhearts ★★★★★
()
Ответ на: комментарий от pawnhearts

переиспользование имен функций - в большинстве случаев ошибка. только в редких случаях (что-то вроде mock) - оправдано

Нормальный язык бы бросал исключение, но давал бы возможность сделать это если надо. Скажем при помощи какого-то библиотечного вызова или спец-синтаксиса.

rsync ★★
() автор топика
Последнее исправление: rsync (всего исправлений: 2)

Так, еще одна бомба под седалища фанбоев «явное лучше неявного». Не, ну правильно, бойлерплейт — самое приятное в программировании. Сидишь фигачишь строчки, думать ваще не нужно. Заходит начальник, ты ему такой: вона скока кода написял насяльника!

bread
()
Ответ на: комментарий от rsync

как правильно передать юзера из теста в тест

за передачу юзера (и вообще чего угодно) из теста в тест нужно принудительно кастрировать

Deleted
()
Ответ на: комментарий от rsync

Функция это объект первого порядка. Ее имя ничем не отличается от другой переменной.

Декораторы, которые ты так не любишь это и есть переиспользование имени @ это просто сахар над

def myfunc():
  pass

myfunc = somefunction(myfunc)

pawnhearts ★★★★★
()
Ответ на: комментарий от pawnhearts

Декораторы, которые ты так не любишь

я знаю что такое декораторы, но это не переиспользование имён, это врапперы.

Врапперы в виде декораторов появились от того что нормальных лямбд нету, были бы нормальные лямбды, то люди бы писали простые функции, принимающие другие функции как аргументы.

но поскольку анонимную функцию объявить по месту нельзя - вот и имаются с декораторами

$.ajax({
  url: url,
  success: function(data) {
     // bla
  }
);

подобный код на питоне невозможен (ибо нормальных лямбд нету), поэтому чтоб хоть где-то заменить лямбды на приемлемый код втащили декораторы:


import pytest
import os

@pytest.mark.skipif(os.getenv('DB', '') == '')
def test_db():
   # тестируем коннектор к БД

были бы лямбды - они бы сюда красиво легли, но их нет.

и соответственно в тестовой системе невозможно нормально написать например блок сабтестов:


subtest 'Потестим некоторые мелочи' => sub {
   plan tests => 2;

   ok $user->foo, 'foo есть';
   is $user->bar, 'baz', 'bar правильно установлен';
}

и так далее.

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

rsync ★★
() автор топика
Последнее исправление: rsync (всего исправлений: 1)
Ответ на: комментарий от pawnhearts

Функция это объект первого порядка. Ее имя ничем не отличается от другой переменной.

это удобно разработчикам языка, но не разработчикам на языке

rsync ★★
() автор топика

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

для неушибленных перлом - в этой прекрасной системе у теста нет явного состояния Skipped и вместо этого стоит костыль, считающий число тестов и орущий что-то невнятное если Failed + Passed ≠ Number of tests? я правильно понял?

Deleted
()
Ответ на: комментарий от Deleted

в этой прекрасной системе у теста нет явного состояния Skipped

нет. еще раз скипанье тестов само по себе, подсчет числа тестов - само по себе.

подсчет числа тестов == тоже тест. в тестировании асинхронных систем бывает весьма важен.

так же в тестировании разного рода парсеров.

например парсер выдает на выход 10 объектов. Каждый объект тестируется на наличие поля id.

по какой-то причине (сломали) парсер в тесте выдал 9 объектов. Тестов получилось меньше чем планировалось, но они все прошли. Весь тест от этого считается не пройденным (план не сошелся)

другой пример: запустили асинхронную систему, протестили ее. Финальное событие из за ошибки пришло раньше промежуточного. Получилось меньшее число тестов чем планировалось. тест не пройден.

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

а скипы тестов тут вообще не при чем

rsync ★★
() автор топика
Последнее исправление: rsync (всего исправлений: 1)
Ответ на: комментарий от rsync

например парсер выдает на выход 10 объектов. Каждый объект тестируется на наличие поля id.

по какой-то причине (сломали) парсер в тесте выдал 9 объектов. Тестов получилось меньше чем планировалось, но они все прошли. Весь тест от этого считается не пройденным (план не сошелся)

то есть тестсьют считается упавшим не из-за того, что упал тест на 10й объект, а из-за того, что план не сошелся? просто ради интереса — какой степени ЧМТ была у того, кто это придумал?

Deleted
()

С удовольствием читаю, как горит у питонистов. Продолжайте наблюдения, там ещё остались дрова!

stevejobs ★★★★☆
()
Ответ на: комментарий от stevejobs

асинхронные системы — последние 5 лет. а кто там в коде системы актив, кто пассив меня вообще не волнует

Deleted
()

передача данных из теста в тест

Отличный источник неуловимых багов :)

question4 ★★★★★
()

передача данных из теста в тест затруднена

Дальше не читал. Видимо фреймворк бьёт тебя по рукам за твои отвратные привычки. Судя по тому, что в коде нет слов fixture/setup/teardown и т.п., дело не в бабине.

Ну и да, я мимокрокодил, но к пайтесту таки есть кукумбер совместимые либы.

pon4ik ★★★★★
()
Ответ на: комментарий от Deleted

ну и чо, тебя не смущает, что коллбэки твои не отрабатывают с написанными там внутри тестами?

stevejobs ★★★★☆
()
Ответ на: комментарий от rsync

подсчет числа тестов == тоже тест. в тестировании асинхронных систем бывает весьма важен.
например парсер выдает на выход 10 объектов. Каждый объект тестируется на наличие поля id.
по какой-то причине (сломали) парсер в тесте выдал 9 объектов. Тестов получилось меньше чем планировалось, но они все прошли. Весь тест от этого считается не пройденным (план не сошелся)

Я не понял, зачем тебе на 10 объектов делать 10 тестов и потом считать сколько у тебя тестов прошло или нет? сделай один тест, который удостоверится что парсер вернул 10 объектов, а потом уже проверяй наличие поля id. Да и вообще количество тестов не должно быть динамическим и зависеть от кода.

Int64 ★★★
()

А чо на перле то не пишется тебе? Если нравится язык, так используй. Если не нравится другой - не используй. Я в бытность студентом 1С быстренько свалил, ибо понял что такую галиматью мне будет отвратно делать. Каждый выбирает себе свой путь, а не ноет. И перл и питон вполне зрелые и достаточные языки

Deleted
()
Ответ на: комментарий от Deleted

есть тестсьют считается упавшим не из-за того, что упал тест на 10й объект, а из-за того, что план не сошелся?

еще раз: тест на 10-й объект мог просто не вызваться, по причине ошибки. а все вызвавшиеся тесты прошли.

и именно для таких проблем - план

rsync ★★
() автор топика
Ответ на: комментарий от Deleted

питонщики в разы дешевле, я теперь ими рулю

ну а чтоб нормально рулить надо питон тоже знать, вот и изучаю

rsync ★★
() автор топика
Ответ на: комментарий от Int64

Я не понял, зачем тебе на 10 объектов делать 10 тестов и потом считать сколько у тебя тестов прошло или нет? сделай один тест, который удостоверится что парсер вернул 10 объектов

однопроходные/потоковые парсеры часто оформляются в виде калбеков и очень похожи на асинхронный код

rsync ★★
() автор топика
Ответ на: комментарий от pon4ik

Судя по тому, что в коде нет слов fixture/setup/teardown

эти декораторы в данном тесте не нужны. обоснуй их необходимость

rsync ★★
() автор топика
Ответ на: комментарий от rsync

тест на 10-й объект мог просто не вызваться, по причине ошибки. а все вызвавшиеся тесты прошли.

и именно для таких проблем - план

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

Deleted
()
Ответ на: комментарий от rsync

ты объяснишь, почему у тебя конкретная ошибка не ловится упавшим тестом, а вместо этого циферки не сходятся? или так и будешь один весь в белом тут кудахтать?

Deleted
()

передача данных из теста в тест затруднена

Чувак, это ж модульные тесты. Там передача «из теста в тест» в идеале вообще должна отсутствовать.

no-such-file ★★★★★
()

В предыдущей серии эпопеи «пригорания перловика» мы узнали что он не знает зачем нужны исключения.

В этой мы узнаем что и что такое юнит-тесты и зачем они нужны он тоже не до конца понимает.

morse ★★★★★
()
Ответ на: комментарий от no-such-file

сохранили юзера и у сохраненного ющера сделали N проверок - типовая тестовая ситуация

«должно отсутствовать» - это «нам колбасы не нужно»

rsync ★★
() автор топика
Ответ на: комментарий от no-such-file

это ж модульные тесты. Там передача «из теста в тест» в идеале вообще должна отсутствовать.

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

bread
()
Ответ на: комментарий от rsync

у сохраненного ющера сделали N проверок

def test_user():
    user = User(...).save()

 -- assert user.id, 'missing id')
 ++ expect(user.id, 'missing id')

 -- assert '@' in user.email
 ++ expect('@' in user.email)

тест на 10-й объект мог просто не вызваться, по причине ошибки. а все вызвавшиеся тесты прошли

что мешает явно проверить что результатов 9 а не 10?

gistart
()

Угадал автора по сравнению языка программирования общего назначения с супцом хаоса для генерации текстовых отчетов.

Слушай, ну если синдром утенка так мешает, пиши на перле и радуйся, не?

Ну или хотя бы свою «аналитику», держи при себе, ограничивайся субъективщиной. Пожалуйста.

t184256 ★★★★★
()
Ответ на: комментарий от gistart

что мешает явно проверить что результатов 9 а не 10?

тестирование асинхронного кода обычно требует усилий посчитать число результатов.

поддержка со стороны тестовой системы позволяет эти усилия не прикладывать

rsync ★★
() автор топика
Ответ на: комментарий от t184256

Ну или хотя бы свою «аналитику»,

по тестам курильщика сказать нечего, что же остается? просто сказать оппоненту: держи мол своё мнение при себе

rsync ★★
() автор топика
Ответ на: комментарий от rsync

требует усилий посчитать число результатов

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

gistart
()
Ответ на: комментарий от gistart

Я ваш код не видел конечно, но это может означать что вы свою систему не контролируете

дело не в контроле, дело в читаемости кода.

посчитать количество вызовов калбеков == глобальная переменная между вызовами (счетчик).

это всегда плохочитабельно.

если подсистема поддержки тестов делает это за тебя, то это прекрасно.

мы же тут как раз обсуждаем проблему что со стороны Python поддержка тестов на таком уровне, чтобы отбить охоту их писать.

rsync ★★
() автор топика
Ответ на: комментарий от rsync

посчитать количество вызовов калбеков == глобальная переменная между вызовами

Это если коллбэки ничего не делают вообще, но тогда непонятно зачем они.

чтобы отбить охоту их писать

Возможно вы не поняли, может быть вам отбивают охоту писать плохой, неконтролируемый код?

gistart
()
Последнее исправление: gistart (всего исправлений: 1)
Ответ на: комментарий от rsync

у сохраненного ющера сделали N проверок - типовая тестовая ситуация

Сохранили куда? Это совсем другой вид тестов. Ты же логику работы объекта тестируешь, а не взаимодействие с БД.

no-such-file ★★★★★
()
Ответ на: комментарий от bread

Группа тестов часто нуждается в каком-то окружении, которое запаришься настраивать для каждого в отдельности

Для этого есть специально обученные макаки люди - тестировщики. А так вообще «да ну нах эти тесты - код запаришься писать, ещё тесты им подавай».

no-such-file ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.