kb 07.01.2013 17:07

Кароч для тех, кому лень было читать (всем кроме меня) вот этот чувак самый умный оказался https://www.tbray.org/ongoing/When/201x/...

1. Statically-typed languages can make unit testing hard, so

2. People adopt dependency injection to work around this, and

3. In a sort of Stockholm-syndrome effect, people argue that DI is A Good Thing and over-use it, to harmful effect.

// дальше только сейчас читаю

1. gds 07.01.2013 17:10

> Statically-typed languages can make unit testing hard

не надо использовать те, которые "can make", йоу.

2. kbgds /1 07.01.2013 17:11

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

3. kbgds /1 07.01.2013 17:11

точнее не с моками а банально "развязывать" вызовы функций напрямую.

4. gdskb /2 07.01.2013 17:12

"из коробки" "ненужно".

5. kbgds /4 07.01.2013 17:12

Можно подробнее?

6. gdskb /3 07.01.2013 17:12

а как это?

7. gdskb /5 07.01.2013 17:13

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

8. kbgds /6 07.01.2013 17:16

Ну, блин, например на время теста подложить нужные результаты вызова http-запроса, вон по ссылке как раз чувак рассматривает такой пример с OAuth. То есть хотя бы на уровне "во время теста вот в этом модуле подменить импорт этого модуля на вот эту хрень" я не видел ЯП как-то (в Си люди извращаются с инклудами, что тоже вариант, конечно :).

9. kbgds /7 07.01.2013 17:16

Ээ. Да нет, вопрос не в том, чтоб они просто были, а в том, чтоб они по понятиям были, собственно для этого и DI.

10. generatorglukoff 07.01.2013 17:20 Досктоп

для С/C++ DI можно реализовать на уровне линкера

11. kbgeneratorglukoff /10 07.01.2013 17:21

Да, я знаю. И понимаю тех, кто этим не занимается :)

12. gdskb /8 07.01.2013 17:30

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

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

А вообще, почему бы не писать сразу правильно? Желательно, формальным образом доказанным образом!11

13. kbgds /12 07.01.2013 17:53

Вот и меня сильно расстраивает, что это не делают частью "фич языка". Я вот даже думал, мол, круто было бы на уровне системы типов давать возможность на время теста объявлять некоторый нечистый код чистым, таким образом система типов бы удостоверилась, что ты всю нечисть на время тестов подменил на свой чистый код. Отсюда и "покрытие нечисти" было бы. Короче, фантазии есть где разыграться.

Сразу писать правильно не умею, формально доказывать корректность тоже еще совершенно не знаю как.

14. gdskb /13 07.01.2013 18:54

> объявлять некоторый нечистый код чистым

я в рот ебал "чистоту кода", если чо. И х-ь в том числе.

Кроме того, #tioeof

Главное — чтобы работало. Функциональный стиль — в плюс к "работает".

Однако прекрасно понимаю, что "подсунуть модуль" может оказаться очень полезным. С другой стороны, чего подсовывать, если тесты таки нужны, и чего бы в этом случае не выделить тестируемое отдельно? Я не знаю, как в петоне с этим дела обстоят, однако.

Про "сразу правильно" — скорее, основываясь на типах, которые не дадут сделать плохого, это работает в языках с богатыми типами. Про "доказывать корректность" — это стоит делать не всегда, чаще типизация и тесты справляются лучше, но учитывать неплохо. Но петонокод доказывать не рекомендую.

15. hirthworkkb /2 07.01.2013 19:55 talkonaut-iphone_5.91_67b1c873

c++, например. google://ubermock

16. hirthworkkb /2 07.01.2013 20:03 talkonaut-iphone_5.91_67b1c873

java + invocation handler

17. kb 08.01.2013 05:32

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

> С другой стороны, чего подсовывать, если тесты таки нужны, и чего бы в этом случае не выделить тестируемое отдельно?

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

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

18. kbhirthwork /15 08.01.2013 05:34

А можно хотя бы readme написать?

19. kbhirthwork /16 08.01.2013 05:40

Ну и снова таки, разве он даёт подменять что-то на уровне импортов?

20. gdskb /17 08.01.2013 05:49

ну, у кого как, а у меня редко получалось что-то нечитаемое из-за параметризации. А польза от неё — ощутимая. Видимо, это субъективный вопрос, фор хум хау.

Питонокод приплетаю к вопросу "формально доказывать корректность", там выше по треду понятно, к чему это. Ну да мелочи.

21. hirthworkkb /18 08.01.2013 06:31 talkonaut-iphone_5.91_67b1c873

оно написано же... :( ща, до работы доеду — сгенерю пдфку и выложу на файлопомойку

22. hirthworkkb /19 08.01.2013 06:32 talkonaut-iphone_5.91_67b1c873

он умеет перехватывать все вызовы к интерфейсу

23. kbhirthwork /22 08.01.2013 08:16

Ну да, вещь хорошая, из джавы почти динамический ЯП делает какой-то, но в случае с модулями и тестируемостью (и главным топиком), к сожалению, не поможет, потому придется снова фабрики, классы и проч. городить ради тестируемости.

24. kbgds /20 08.01.2013 08:24

Ну как тебе еще объяснить. Если бы в джава мире все писали не на джаве а на хаскеле (как буду знать окамл — напишу тебе на окамле :), то давно бы все писали вместо

foo :: X → Y
foo x = (f1 x) + (f2 x) — (f3 x)

Как-то так:

create_foo f1 f2 f3 = (\x → (f1 x) + (f2 x) — (f3 x))

и потом в рантайме делали бы

foo = create_foo f1 f2 f3

И кучу тестов бы понаписали, в каджом создавая foo заново чтоб со своими f1 f2 и f3 было. То есть фабрики чисто ради тестируемости делаются, а теперь еще приговаривается, что дикаплинг — это круто (даже когда он не нужен), и специальные фреймворки, позволяющие так повсеместно писать есть.

В общем, я лично считаю, что надо на уровне языка подобную подмену f1, f2 и f3 разрешать делать на время теста, а не делать язык настолько "динамичным".

25. hirthworkhirthwork /21 08.01.2013 09:04 mcabber

1. обещанная документация: https://docs.google.com/open?id=0B6ttfyN... можно сразу переходить к третьей странице
2. тестилось только под x86, и то давно
3. сейчас не работает вообще, хз почему, видно что-то забыл поправить
4. чинить нет времени, так что на чтение документации ты впустую потратишь время

26. kbhirthwork /25 08.01.2013 09:37

Ага, ну убермок крут, конечно, но я так понял всё равно всё пляшет от #include'ов, а затем перекрытия нужных функций :)

27. hirthworkkb /26 08.01.2013 09:38 mcabber

ну да, а как ты будешь перекрывать функцию не заинклудив её определение? (особенно с учётом extern C перед ней)

28. kbhirthwork /27 08.01.2013 09:40 04a3831c

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

29. gdskb /24 08.01.2013 12:32

ну так это негодная параметризация какая-то! Хотя идею понял. Рад, что объяснил.

в окамле обычной "единицей абстракции апи" является модуль, и вот, для нужных модулей (в том числе из стандартной библиотеки, типа работы с файлами или сетью) вполне можно написать замену и линковать её для тестов. То есть, дело в том, что именно будем линковать. Или в реальных обстоятельствах это хуёвое решение? (про подмену f1, f2, f3 идею понял, но представим себе какой-нибудь другой путь, если интересно почесать репу. Если я заебал — забьём.)

30. kbgds /29 08.01.2013 13:54

Ну линковать-то можно почти везде, наверное и в джаве можно, но ведь:

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

То есть, я конечно не пробовал, но сдаётся мне, что с линкованием будет ну очень геморно.

31. kbgds /29 08.01.2013 14:05

То есть, чисто чтоб ты представлял как это происходит в питоне (без использования извращений в виде DI):

есть у тебя модуль psto.posts.bl (бизнес-логика) и в нём функция, скажем, ban_user.

def ban_user(user):
ok = deactivate_user(user)
if not ok:
return
notify_about_ban(user)
if user.age < 10:
user.status = "loh"
elif 10 < user.age < 20:
user.status = "mudak"
else:
user.status = "pots"
user.save()

Так вот, цель у тебя — протестировать очень сложный механизм с заданием статуса юзеру ("loh", "mudak" и "pots"), при этом ты не хочешь чтоб deactivate_user, notify_about_ban и прочие спец-эффекты (вроде бы их здесь нет) как-то на твое тестирование влияли, потому ты просто задашь их поведение явно.

В питоне это выглядело бы как-то так:

class TestBanUser(TestCase):
@patch("deactivate_user", return_value=True)
@patch("notify_about_ban", return_value=None)
def test_should_set_status_to_loh(self):
user = UserFactory.create()
user.age = 8

ban_user(user)

assert_equal(user.status, "loh")

То есть, видишь как красиво (на самом деле еще красивее) ты:
1. определяешь поведение нетестируемых частей (функций deactivate_user, notify_about_ban).
2. заменяешь их на "моки", таким образом они мгновенно отрабатывают (и не шлют имейлов, не лезут в БД и так далее).

Таким образом ты тестируешь только часть логики, которая работает с user.status, и знаешь что делает/возвращает всё остальное.

Как сделать подобного рода вещи только через линковки я не знаю.

32. kbkb /31 08.01.2013 14:07

читабельная паста
http://paste.ubuntu.com/1509615/

33. gdskb /32 08.01.2013 14:37

(да и так вполне читаемо было!)
Идею понял.
Да, тут либо патчить, либо параметризовывать.
А вот как мне нравится решать подобное в coq: https://gist.github.com/b3b1af7808e8bfbc... (там упрощение в плане сайд-эффектов и в плане if, но можно сделать и более-менее по-честному). Доказательство состоит из "взять все утверждения в контекст доказательств; поупрощать всё что можно; подобрать автоматически терм для очевидного факта" (этот очевидный факт — факт того, что какие-то actions таки могут быть равны тем actions, которые реально выдаёт ban_user).

34. kbgds /33 09.01.2013 08:25

Въехать скоро не обещаю, но попробую в ближайшее время.

36. ulidtkohirthwork /25 09.01.2013 23:08

лол

37. kbgds /33 11.01.2013 10:53

Ага, посмотрел. Ну выглядит красиво, конечно, жаль я понятия не имею как оно выводить это будет :) В будущем бы с удовольствием изучал такое.

38. gdskb /37 11.01.2013 11:47

а что тут выводить? Из вывода тут только "exists", которое говорит "есть какой-то список действий, не важно какой".
А так — рекомендую связку coq + ocaml для написания хитровыебанного кода, если нужно очень дохуя гарантий. Если чо, помогу, чем смогу. Вдобавок, хорошая книжка по coq находится тут: http://adam.chlipala.net/cpdt/ .

39. gdskb /37 11.01.2013 11:55

кстати вот, про "очень дохуя гарантий" это не шутка. Иногда их настолько дохуя, что заёбываешься. Когда я заёбываюсь с coq, беру окамл и говнокодю на нём. То есть, coq далеко не всегда нужен. Но знать о нём и немножко его уметь — это всяко плюс.

40. fractal 22.01.2013 15:03

DI, моки… Действительно, чё с ними носятся, это ж всего-навсего higher-order functions & closures!

41. kbfractal /40 23.01.2013 04:14

В функциональном программировании скорее всего вместо фабрик ты бы делал higher-order functions и замыкания, да (ну, еще, возможно, какой-нибудь message-passing чтоб описать одной функцией сразу целый интерфейс, чтоб заменить объект).

Но при чем здесь это? С замыканиями и higher-order functions проблема-то остаётся абсолютно той же, пример описан в /24

42. ulidtkokb /41 23.01.2013 10:07

благодаря first-class functions /24 решается обыкновенным карированием

то есть, просто не вижу проблемы в этой твоей жалобе, всё работает и выглядит норм

43. kbulidtko /42 23.01.2013 10:15

каким карированием? что решается? что ты несёш вообще? при чем здесь это всё?

44. hirthworkkb /43 23.01.2013 10:17 mcabber

COCOCONSTRUTIVE DEBATE!!1

45. kbhirthwork /44 23.01.2013 10:19 04a3831c

да вообще пиздец. обсуждаем одно, и тут врывается какой-то тип и на пример, приведённый для объяснения проблемы, отвечает фразой "это решается ...". что, блять, решается? здесь /0 решается, вообще-то.

Do you really want to delete ?