Как стать автором
Обновить
0
0

code writer

Отправить сообщение

В конкретном случае с турникетом и оплатой — надо копить стейт, как я написал прямо в тексте, чтобы можно было бросить 10 жетонов, а потом пройти вдесятером.

Так это же и есть инкапсуляция.

Вот у вас есть Турникет.

У него есть два состояния - Открыт, Закрыт.

И события - Жетон, Карта, Пассажир.

Есть внутренние данные (инкапсулированные) - Кол-во проходов.

Работает просто:

  • Открыт + Жетон или Карта -> Открыт (кол-во проходов +1)

  • Закрыт + Жетон или Карта -> Открыт (кол-во проходов +1)

  • Открыт + Пассажир -> Открыт или Закрыт (в зависимоти от врутренних/инкапсулированных данных)

  • Закрыт + Пассажир -> Исключение!

Вот и получается, что наружу "кол-во проходов" не торчит, т.е. инкапсулированно.

При этом вролне нормально иметь метод displayInfo где мы можем показать сколько проходов отсталось. Или добавить метод reset если у нас бывают какие-то ситуации, что турникет заглючил и его нужно перезагрузить.

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

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

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

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

Интересно было-бы пример посмотреть. Особенно - что было "неудобным" и как выглядит "привычный API".

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

Мне интересно сколько прособеседовали, чтобы хоть одного нанять. Я когда работал в компании которая жила "на свои", а не на инвестиции у нас тоже была статистика найма 1-2 на 100. Это как раз нормально когда у вас нет показателя найма и совсем небольшая текучка.

То, что вы описываете называется капитализм. Есть теория, что капитализма больше нет и мы в чем-то новом. Один товарищь называет это - техно-феодализм.

Я не говорю, что это правда, но по крайней мере объясняет почему это так работает.

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


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


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


Для маленьких проектов посмотрите в сторону Outside In тестов. Т.е. пишите только очень высокоуревнивые тесты, они быстро пишутся и покрывают очень большую площать кода.


Последние лет 5 я пишу только такие тесты и мне кажется, что их достаточно. Вот пример тестов которые пишу:


def when_user_created_welcome_message_sent(client):
  client.post("/users", { "name": "Vadim", "age": 18 })
  user = User.objects.get({name: "Vadim"})

  assert Email.objects.filter({type: "welcome", user_id: user.id}).exists()

Суть Outside In тестов, то вы ничего не мокаете и просто дергаете API и потом проверяете то, что должно произойти, т.е поведение системы.
Получается, что тут не важно как все реализовано, какие методы вызывались. Просто проверяете: если я отправил вот такой JSON на такой URL, то мне пришел ответ 200 и данные которые я ожидаю.

"Да, конечно делай, если это не увеличит время выполнения задачи."… И зачем оно мне тогда?

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

А могли бы вы рассказать как пример с if-else нарушает SOLID и какой именно принцип?

табу на on-calls и на любые действия вне графика

С on-call, по крайней мере где я работал, проблем небыло, правда есть четкие правила как время на on-call компенсируется (бывают выходные, бывают премии и тд).
Поработать в нерабочие часы тоже могут, но это исключительно личная инициатива, а просить поработать вне графика… тут тебя просто не поймут, нет такого. Хм… получается это и правда табу.
Хотя я всего в 3х компаниях работал, так что все мои рассуждения — это довольно маленькая выборка.
Вон в Испании, 35 часов в неделю, и куча проблем, если надо кого-то вызвать на овертайм или вне рабочего времени.


Не, в Испанском IT работают по 40 часов в неделю и увольняют одним днем.
За 5 лет в Испании я еще не встречал компанию которая работает по сокращенной неделе.
Почему вы считаете, что по фэн-шую в TDD должны быть именно модульные тесты?
Я понимаю, что Дядя Боб рассказывает, что TDD это когда вы пишете сначала очень простой тест (за минутку), потом супер простоую имплементацию (опять за минутку) чтобы тест стал зеленым. И повторяете такие же маленькие итерации многократно, потихоньку усложняя тесты и соответственно имплементацию.
Для обучения TDD в домашних условиях — это норм подход, но практиковать его на реальном проекте — боль, т.к. при изменениях слишком много тестов нужно будет постоянно переписывать.

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

По моему мнению TDD — это когда сначала тест, потом код. И все, больше нет никаких дополнительный условий.

И с другой стороны, почему мой тест не модульный? Я просто тестирую сервис (вместе с БД, очередью, кэшем и т.д) как один независимый модуль.
Ведь что угодно может быть модулем — функция, класс, библиотека, сервис, множество сервисов как единая система.

TDD в силу своей конструкции навязывает восходящий метод проектирования ...

Периодически слышу такое-же мнение, но не могу понять как люди пришли к этому заключению?

Вот я считаю, что 100% использую TDD в варианте "Outside In".

Это когда пишутся только высокоуровние тесты, которые тестируют пользовательские сценарии.

Вот пример стандартного теста (пример на python с pytest, т.к. на java или чем-то подобном будет лишний бойлерплэйт):

def test_answer_to_question(client, pub_sub_mock):
    # given
    question = Question.objects.create(...)
    
    # when
    url = f"/api/v1/questions/{question.id}"
    response = client.put(url, { "answer": "some answer" })

    # then
    assert response.status_code == 200
    question.refresh_from_db() # тут перечитываем вопрос из БД
    assert question.answer == "some answer"
    pub_sub_mock.assert_called_once_with({
        "event": "question.answered",
        "id": question.id
    })

Тест делает следующее:

  • приводим систему в некое начальное состояние

    • сохраняем в БД объект Question

  • выполняем над ней определенное действие

    • отвечаем на вопрос делая PUT запрос в API

  • проверяем только сайд-эффекты

    • API вернул код 200

    • свойство объекта Question изменено в БД

    • Event был отправлен во очередь сообщений

На последнем проеке таких тестов было процентов 95, и покрывали 100% бизнес сценариев. Остальные 5% тесвов покрывали разные технические штуки.

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

А главное - простота рефакторинга, можно просто выбросить текущую имплементацию и переписать заново и тесты не нужно переписывать.

Не до конца я понял вашу идею. Как-то очень запутанно получилось.
Я бы сделак что-то вроде:

# инкапсулируем логику работы с API  в отдельный класс
class Api:
  def fetch_data(): ...
  def save_data(data:): ...

# это по сути основная бизнес логика приложения
def update_data(data):
  return [fileld.upper() for field in data]

# ну а это наш контроллер
def do_work():
  api = Api()
  data = api.fetch_data()
  updated_data = update_data(data)
  api.save(updated_data)


Тестировал бы используя подход outside-in, т.е. мокировал бы библиотеку requests и проверял бы, что мы отправляем то, что API ожидает.

def test_api(requests_mock):
  do_work()
  requests_mock.assert_called_with(['FIELD1', 'FIELD2', 'FIELD3'])


Ну и если `data` это достаточно сложный объект, мжно добавиь `dataclass` и дополнительно сериализацию/десериализацию, в таком случае из `Api` будем возвращать готовые объекты, а не словарь, соответственно можно будет в этот класс добавить функции манипуляции с объектом.

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность