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

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

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

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

Часто требования намеренно расплывчаты, чтобы оценить, как вы справляетесь с двусмысленностью. Или может быть, они просто не очень хорошо написали требования 🤷‍♀… но в любом случае вы можете продемонстрировать, что можете с этим справиться.

Идеальный подход, если никто не может спросить или они сознательно «не могут» дать ответ, - это придерживаться того или иного предположения ... и

зафиксируйте это предположение в тесте.

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

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

Итак ... перейдем к делу:

Напишите программу, которая печатает числа от 1 до 100. Но для чисел, кратных трем, выведите Fizz вместо числа, а для чисел, кратных пяти, выведите Buzz. Для чисел, кратных трем и пяти, выведите FizzBuzz.

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

Большинство кандидатов засучат рукава и запрограммируют решение. Они могут даже написать пару юнит-тестов. Но именно здесь можно действительно выделиться из толпы.

Время для быстрой настройки

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

Предполагая, что на компьютере для интервью уже установлен Node / NPM:

$ mkdir FizzBuzz
$ cd FizzBuzz
$ npm install --save-dev jest

Затем создайте main.js, чтобы содержать тестируемый код, и main.test.js, чтобы содержать тесты.

Добавьте в main.js функцию-заполнитель - то, что может вызывать ваш тест, чтобы вы знали, что все настроено правильно:

И не забывайте package.json:

Перечислите тестовые сценарии

Теперь вы можете приступить к определению некоторых тестовых сценариев. Просто превратите бизнес-правила в модульные тесты:

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

Однако будьте осторожны, если вы запустите эти пустые тесты с npm run test, вы получите удивительный результат:

Чтобы сделать процесс более систематическим, добавьте .skip к каждому тесту - test.skip(…), чтобы Jest не считал нереализованные тесты пройденными:

Затем, когда вы перейдете к каждому тесту по очереди, просто удалите .skip.

Итак, давайте начнем кодировать тестовые сценарии по одному. Вот первый:

Наверное, не тот, которого вы ожидали? Извините за кривую голову ... но когда вы пишете тесты, всегда старайтесь быть злым гением (мвахахахааа и т. Д.). Глубоко подумайте о том, что может пойти не так… и добавьте сценарии тестирования «неудачный путь», чтобы ваш код оставался честным.

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

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

Кстати, этот подход является центральным принципом предметно-ориентированного тестирования (DOT).

В процессе добавления нового «злого» теста я подумал, что нам также нужно обеспечить входные значения от 1 до 100, учитывая исходную спецификацию. Никогда не поздно, значит:

И, привет, я добавил еще несколько входов в сценарий «отклоняет нечисловые». В зависимости от ваших предпочтений или стиля модульного тестирования вы можете предпочесть иметь только одно предложение expect в каждой тестовой функции. Но меня устраивает вышеизложенное, поскольку каждая функция тестирования проверяет один сценарий, основанный на требованиях.

Дайте тестам быстрый запуск, чтобы показать сбой новых сценариев, затем добавьте этот код, чтобы последние сценарии прошли:

Таким образом, у вас все еще есть ветвь кода «не реализована», но функция теперь проверяет ввод и выдает ошибку, если он недействителен.

👉 Обратите внимание: я немного продвинулся вперед с приведенным выше кодом и выполнил два теста за один раз. Если вы придерживаетесь чистого TDD, вы можете сначала добавить код для прохождения первого теста, а затем добавить код для прохождения второго теста… но это полностью зависит от вас.

На практике ДОТ не так строги. Поскольку у вас уже есть полный набор тестовых сценариев и вы работаете над ними, вы можете позволить себе немного ускорить внедрение тестов и кода.

Итак, перейдем к следующему тесту и коду, чтобы он прошел:

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

Но подождите ...

Вам может показаться, что средство сопоставления тестов неточно. И это тоже выглядит неуклюже. Действительно ли нас волнует тот факт, что результат - это число? Почему бы просто не ожидать, что результат будет таким же, как и переданное число, которое, как мы уже знаем, является числом?

На самом деле, зачем проверять только одно значение? Как тест, который действительно проверяет «любые допустимые числа, не кратные 3 или 5», он довольно ограничен по объему.

Итак, после быстрого «обновления» тестового кода у нас теперь есть эта красота:

🐶 Если вы хотите по-настоящему испытать этого щенка, тестирование на основе свойств может сгенерировать множество подходящих входных значений. Для FizzBuzz это было бы излишним, но все равно об этом стоит знать.

Запустив то, что у нас есть, теперь получаем:

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

Вот завершенный набор тестов:

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

Однако программирование - это плавный процесс; мы исправляем или улучшаем вещи по мере продвижения, как только мы их замечаем.

И готовая, супербезопасная тестируемая функция выглядит так:

Но есть еще одна вещь. Мы должны помнить о выполнении первоначального требования:

Прокрутите цикл от 1 до 100 и выведите что-нибудь для каждого числа.

Пока мы этого не сделали. Провал собеседования! 😬

Давайте исправим это сейчас.

Конечно, печать на консоли - это побочный эффект; это сложно проверить. Мы могли предоставить настраиваемую функцию печати, а затем предоставить имитацию / поддельную версию из тестов, которая проверяет, вызывается ли она каждый раз. Но, черт возьми, это звучит ужасно. Давай не будем этого делать.

Вместо этого вы можете просто добавить в main.js следующий код:

Теперь, чтобы успешно пройти собеседование, запустите программу:

node main.js

Вот результат:

Выполнив свою скудную задачу по кодированию, жизненно важно, чтобы вы сейчас хлопнули обоими кулаками по столу и крикнули: «Я правлю!»

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

На бонусные баллы

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

Например, если у задания более сложные требования, вы можете начать с некоторых тестов в стиле BDD. У Jest есть хороший плагин jest-cucumber, который позволяет выполнять сценарии Gherkin (задано / когда / тогда) для этого стиля тестирования.

DOT также работает на уровне BDD или приемочного тестирования. (Фактически он работает на 3-х уровнях).

Еще одна вещь

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

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

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

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