170 похожих чатов

Вот недавно вообще задумался, есть ооп, есть функциональный стиль, где

пишем только чистые функции. Вроде как ооп прикольно тем, что создаем абстракцию какой-то сущности с публичным интерфейсом для работы. Но в ооп почти все методы не чистые (из-за работы с this), из-за этого сложно юнит тестировать. Как найти эту золотую середину? Смириться с тем, что тру юнит тестами нельзя покрыть класс?

46 ответов

45 просмотров

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

у меня ФП и ООП прекрасно существуют совместно

Прям без переменных с функторами и монадами?

.sha- Автор вопроса

Да, например, я могу заинжектить приватный внутренний массив, в котором Colection хранит элементы Это решит проблему при тестировании Но сделает использование класса очень неудобным, т.к. пользователям вообще не нужны эти детали, где именно collection хранит свои элементы

зачем тут лишняя терминология? просто функции как значения и функции с методами

Ну это не ФП, дело не в терминологии. Просто ты используешь лямбда функции и все.

а можно заижектить не в самой коллекции, а в её потомке и использовать для тестирования его?

глянь на это https://github.com/Svoloch/etc-js и скажи это ФП или ООП

.sha- Автор вопроса

Тогда тест (вот эта часть с потомком и инжектом) зависит от внутренностей класса. С таким успехом я могу тупо взять и заассертить приватное поле

модифицируй именно класс коллекции чтобы он сам проверял что в нём поменялось

Похоже функциональный стиль но нехватает таких вещей как композиция функций, я бы рекомендовал в ФП стиле рамду https://ramdajs.com/docs/

композиция это then (если я правильно понимаю это понятие)

Если бы фп и ооп прекрасно существовали по отдельности, в джаве бы на было лямбд, а в c# linq и делегатов

.sha- Автор вопроса

Хм. Это как?

В JS уже есть оператор композиции функций — |>

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

на данном этапе предпочитаю избегать всего что за пределами шестого стандарта

.sha- Автор вопроса

Ну вот minimal case, с которым возникла проблема у меня class Collection { _items = []; push(item) { this._items.push(item); } } // test describe(‘Collection’, () => { it(’should push items’, () => { const collection = new Collection(); collection.push(‘something’); assert(); // assert what?? }) })

так это не js, я то думаю откуда такие странные проблемы

.sha- Автор вопроса

Да блин, сейчас исправлю на js, по привычке написал на ts

.sha- Автор вопроса

Исправил

да оно и в js скомпилит так же, приватные поля там и так будут видны (или я ошибаюсь?)

.sha- Автор вопроса

Да, будут видны приватные поля, если только стандарт не выставлен на ES-из-последних Но не суть, вопрос же не в этом )

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

.sha- Автор вопроса

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

проверяйте приватные поля класса через Reflect.get

.sha- Автор вопроса

Проблема не в том, что я не могу получить приватные поля, а в том, что это делать неправильно в юнит тестах

Ты ж наверное не просто так туда добавляешь items наверное как то можешь их читать? Вот и надо тестировать метод который читает, либо ты вызываешь какой то другой класс, тогда с помощью. spy тестируешь что вызванному методу передались нужные items

Это плохая идея, тестировать надо только публичный интерфейс

.sha- Автор вопроса

Да, я могу сделать collection.push(…); assert.deepEqual(collection.getAll()); Просто это уже не юнит тест — если что-то сломаем в getAll, то наш тест push упадет, хотя не должен. Но похоже, это единственный выход

Юнит в ООП это класс а не метод

.sha- Автор вопроса

Хммм, а вот это многое объясняет..! Если это действительно так, то я могу в юнитах спокойно использовать несколько методов и не стабить если один метод использует другие этого класса? А где можно почитать про эту концепцию

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

В контексте юнит тестов доступ к приватным полям класса это рефлексия, поэтому вполне нормально

.sha- Автор вопроса

Я просто в таких случаях: class Class { // … method() { this.anotherMethod1(); this.anotherMethod2(); } } Стабал anotherMethod1 и 2 при тестировании method. То есть этого делать не стоит?

Стабить надо только внешние зависимости а тестировать через публичный интерфейс

.sha- Автор вопроса

Я руководствовался тем, что юнит — один метод, поэтому стабал (изменения в anotherMethod не приводят к падению теста method)

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

.sha- Автор вопроса

А почему так тесты будут более хрупкими? Типа если поменяется реализация anotherMethod, а стаб останется старым, то тесты начнут падать?

Ну да надо поддерживать больше стабов и при каждом рефакторинге приватных методов рефакторить еще и тест

.sha- Автор вопроса

Нет, приватные я не стабаю, только публичные

А зачем стабить публичные если их и так можно проверить?

.sha- Автор вопроса

Ну типа следуя концепции юнит теста, что тестируем ровно 1 вещь Если юнит — метод, тогда при падении теста я со 100% увернностью могу сказать, что проблема в этом конкретном методе, и не затрагивает другие

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

.sha- Автор вопроса

Понял, спасибо огромное за советы ) А интересно, возможно ли (и стоит ли) сделать такой класс, как приведенный выше collection, immutable?

Ну это зависит от того какие требования к программе, не забывай что immutable сущности жрут значительно больше пямяти

Похожие вопросы

Обсуждают сегодня

Господа, а что сейчас вообще с рынком труда на делфи происходит? Какова ситуация?
Rꙮman Yankꙮvsky
29
А вообще, что может смущать в самой Julia - бы сказал, что нет единого стандартного подхода по многим моментам, поэтому многое выглядит как "хаки" и произвол. Короче говоря, с...
Viktor G.
2
30500 за редактор? )
Владимир
47
а через ESC-код ?
Alexey Kulakov
29
Чёт не понял, я ж правильной функцией воспользовался чтобы вывести отладочную информацию? но что-то она не ловится
notme
18
У меня есть функция где происходит это: write_bit(buffer, 1); write_bit(buffer, 0); write_bit(buffer, 1); write_bit(buffer, 1); write_bit(buffer, 1); w...
~
14
Добрый день! Скажите пожалуйста, а какие программы вы бы рекомендовали написать для того, чтобы научиться управлять памятью? Можно написать динамический массив, можно связный ...
Филипп
7
Недавно Google Project Zero нашёл багу в SQLite с помощью LLM, о чём достаточно было шумно в определённых интернетах, которые сопровождались рассказами, что скоро всех "ибешни...
Alex Sherbakov
5
Ребят в СИ можно реализовать ООП?
Николай
33
https://github.com/erlang/otp/blob/OTP-27.1/lib/kernel/src/logger_h_common.erl#L174 https://github.com/erlang/otp/blob/OTP-27.1/lib/kernel/src/logger_olp.erl#L76 15 лет назад...
Maksim Lapshin
20
Карта сайта