Привет! Принесли вам новую подборку задачек с собеседований.
Задачки — самые что ни на есть настоящие, любезно предоставленные Леруа Мерлен — крупнейшей в России компанией формата DIY.
Леруа Мерлен — компания с серьёзным ИТ, в том числе внутренней разработкой. Именно они создают самый современный и технологичный ритейл в России.
Если с лёгкостью решите все задачи из выпуска, советую тогда заодно ознакомиться со списком открытых вакансий в Леруа, мало ли, вдруг это судьба?
Для тех, у кого решение вызовет сложности, правильные ответы, как всегда, опубликуются через неделю. Кстати, ответы на задачи из предыдущего выпуска — здесь.
1. Последовательность из 3 чисел
1. Заголовки в REST
2. Тестирование связки сервисов
Задачки — самые что ни на есть настоящие, любезно предоставленные Леруа Мерлен — крупнейшей в России компанией формата DIY.
Леруа Мерлен — компания с серьёзным ИТ, в том числе внутренней разработкой. Именно они создают самый современный и технологичный ритейл в России.
Если с лёгкостью решите все задачи из выпуска, советую тогда заодно ознакомиться со списком открытых вакансий в Леруа, мало ли, вдруг это судьба?
Для тех, у кого решение вызовет сложности, правильные ответы, как всегда, опубликуются через неделю. Кстати, ответы на задачи из предыдущего выпуска — здесь.
Вопросы
1. Последовательность из 3 чисел
Я загадываю некоторое правило, по которому буду строить последовательность из 3 чисел и называю последовательность, которая подходит под мое правило, например, 3-6-9. Мне можно предлагать примеры последовательностей, а я буду говорить, подходит ли такая последовательность под мои правила или нет. Нужно понять, какое правило было загадано. Ограничений на количество попыток с последовательностями нет.
P.S. конкретной последовательности в примере не загадано, интересна сама логика решения, интересно, по какому принципу будут подобраны дополнительные примеры последовательностей и какие будут возникать вопросы.
Задачи
1. Заголовки в REST
Существует REST API, который при запросе GET /hello получает в одном из заголовков значение, являющееся числом Int32. Какие кейсы нужно описать, чтобы проверить корректность обработки сервисом этого заголовка?
2. Тестирование связки сервисов
Есть сервис A. Его задача — управлять сложными процессами, например, возврата денег за отмененный заказ. Когда в сервис поступает запрос об отмене заказа и возврате денег, сервис A делает три последовательных запроса в сервисы B, C и D. Между запросами сервис A валидирует полученные данные и может отменить возврат денег, если заказ не соответствует условиям или если произошла техническая ошибка. Какие тесты нужно написать, чтобы убедиться, что процесс возврата денег работает корректно при корректных данных (заказ соответствует условиям, технических ошибок не происходит), и быть уверенными, что выпуск новой версии любого из четырех сервисов не приведет к регрессу?
Ответы
Вопрос 1
Неправильное решение
Неправильным подходом будет сразу предположить интуитивное правило, выводимое из примера (это числа, которые делятся на 3, идут по возрастанию друг за другом) и предлагать только примеры, которые подтверждают предположение, 12-15-18, 21-24-27.
Правильное решение
Нужно предлагать не только последовательности, подтверждающие догадку, но прежде всего те, которые ее опровергают. Могут ли числа не делиться на 3? Могут ли числа идти не по возрастанию? Могут ли числа быть отрицательными? Могут ли числа быть простыми? Могут ли числа быть не целыми, а рациональными или даже комплексными?
Неправильным подходом будет сразу предположить интуитивное правило, выводимое из примера (это числа, которые делятся на 3, идут по возрастанию друг за другом) и предлагать только примеры, которые подтверждают предположение, 12-15-18, 21-24-27.
Правильное решение
Нужно предлагать не только последовательности, подтверждающие догадку, но прежде всего те, которые ее опровергают. Могут ли числа не делиться на 3? Могут ли числа идти не по возрастанию? Могут ли числа быть отрицательными? Могут ли числа быть простыми? Могут ли числа быть не целыми, а рациональными или даже комплексными?
Задача 1
Неправильное решение
Предложить только положительный случай (значение заголовка равно 10 или 100000) или только случаи, которые рассматривают заголовок только как число (например, проверить положительное число в заголовке, отрицательное и 0) или отсутствие заголовка, если он обязателен.
Правильное решение
Заголовки представляются в строковом виде. Соответственно, когда сервис получает запрос, он приводит строку к целочисленному представлению. Нужно проверить, что сервис корректно обрабатывает ошибку, когда:
сервис получает строку, он обрабатывает это как ошибку, и выдает соответствующий ответ;
происходит несовпадение кодировок;
в заголовке передано в строковом виде число, большее чем INT_MAX или меньшее, чем INT_MIN;
заголовок присутствует в запросе более одного раза.
Дополнительные возможные проверки — допускаем ли мы возможность передавать число не в десятичном формате? Существует ли в языке, на котором написан сервис, ограничение на максимальную длину строки, за пределы которой может выйти заголовок?
Предложить только положительный случай (значение заголовка равно 10 или 100000) или только случаи, которые рассматривают заголовок только как число (например, проверить положительное число в заголовке, отрицательное и 0) или отсутствие заголовка, если он обязателен.
Правильное решение
Заголовки представляются в строковом виде. Соответственно, когда сервис получает запрос, он приводит строку к целочисленному представлению. Нужно проверить, что сервис корректно обрабатывает ошибку, когда:
сервис получает строку, он обрабатывает это как ошибку, и выдает соответствующий ответ;
происходит несовпадение кодировок;
в заголовке передано в строковом виде число, большее чем INT_MAX или меньшее, чем INT_MIN;
заголовок присутствует в запросе более одного раза.
Дополнительные возможные проверки — допускаем ли мы возможность передавать число не в десятичном формате? Существует ли в языке, на котором написан сервис, ограничение на максимальную длину строки, за пределы которой может выйти заголовок?
Задача 2
Неправильное решение
Неправильным решением будет написать один тест, который будет запущен на реальных сервисах A, B, C и D, и остановиться на этом. Такой тест нужен, но его недостаточно, чтобы протестировать всю систему целиком — такой тест может сломаться из-за ошибки в любом из сервисов. В случае, если тест сломался из-за ошибки в сервисе B, то он остановится на этом шаге. Пока сервис B чинят, мы не можем проверить сервисы C и D, а значит, для них заблокирован релиз.
Правильное решение
Кроме теста с реальными сервисами нам нужно проверить связи между сервисами A и B, A и C, A и D по отдельности. Мы будем поднимать реальные сервисы вместе с моками, чтобы проверить каждый по отдельности. Тогда, если сервис B сломается, и вместе с ним сломается e2e-тест, мы сможем выпустить сервисы C и D уверенными, что они не привнесли дополнительные ошибки, проверить которые мы не можем.
Можно начертить такую таблицу с тестами:
В данном случае функциональные тесты нужны нам, чтобы проверить корректность работы каждого их сервисов. В рамках задачи мы фокусируемся только на позитивном сценарии, но вообще функциональные тесты будут также проверять негативные сценарии и обработку ошибок (как в задаче 1). Эти тесты сломаются, если в сервис привнесли “случайные” ошибки (например, в рамках рефакторинга, если он делался при недостаточном покрытии кода юнит-тестами), если был нарушен контракт и т.д. В рамках процесса непрерывной интеграции эти тесты должны запускаться первыми.
Интеграционные тесты нужны для того, чтобы проверить, что сервисы соблюдают контракты друг друга. Они сломаются, если контракт нарушится одной из сторон — будет переименовано поле, необязательное поле станет обязательным и т.д. Эти тесты должны запускаться после того, как прошли функциональные тесты на выпускаемый сервис.
E2e-тест — самый большой и ненадежный из всех. Он тестирует реальную бизнес-логику системы, и должен запускаться последним, если все предыдущие тесты не содержат ошибок.
***
Еще одним правильным решением будет предложить контрактное тестирование — когда сервис A поднимается с моками сервисов B, C и D. Моки сервисов записывают запросы, которые приходят от сервиса A. После этого поднимаются реальные сервисы B, C и D, и им отправляются записанные запросы. Ответы сервисов B, C и D должны совпадать с теми, которые дали моки
Неправильным решением будет написать один тест, который будет запущен на реальных сервисах A, B, C и D, и остановиться на этом. Такой тест нужен, но его недостаточно, чтобы протестировать всю систему целиком — такой тест может сломаться из-за ошибки в любом из сервисов. В случае, если тест сломался из-за ошибки в сервисе B, то он остановится на этом шаге. Пока сервис B чинят, мы не можем проверить сервисы C и D, а значит, для них заблокирован релиз.
Правильное решение
Кроме теста с реальными сервисами нам нужно проверить связи между сервисами A и B, A и C, A и D по отдельности. Мы будем поднимать реальные сервисы вместе с моками, чтобы проверить каждый по отдельности. Тогда, если сервис B сломается, и вместе с ним сломается e2e-тест, мы сможем выпустить сервисы C и D уверенными, что они не привнесли дополнительные ошибки, проверить которые мы не можем.
Можно начертить такую таблицу с тестами:
В данном случае функциональные тесты нужны нам, чтобы проверить корректность работы каждого их сервисов. В рамках задачи мы фокусируемся только на позитивном сценарии, но вообще функциональные тесты будут также проверять негативные сценарии и обработку ошибок (как в задаче 1). Эти тесты сломаются, если в сервис привнесли “случайные” ошибки (например, в рамках рефакторинга, если он делался при недостаточном покрытии кода юнит-тестами), если был нарушен контракт и т.д. В рамках процесса непрерывной интеграции эти тесты должны запускаться первыми.
Интеграционные тесты нужны для того, чтобы проверить, что сервисы соблюдают контракты друг друга. Они сломаются, если контракт нарушится одной из сторон — будет переименовано поле, необязательное поле станет обязательным и т.д. Эти тесты должны запускаться после того, как прошли функциональные тесты на выпускаемый сервис.
E2e-тест — самый большой и ненадежный из всех. Он тестирует реальную бизнес-логику системы, и должен запускаться последним, если все предыдущие тесты не содержат ошибок.
***
Еще одним правильным решением будет предложить контрактное тестирование — когда сервис A поднимается с моками сервисов B, C и D. Моки сервисов записывают запросы, которые приходят от сервиса A. После этого поднимаются реальные сервисы B, C и D, и им отправляются записанные запросы. Ответы сервисов B, C и D должны совпадать с теми, которые дали моки