Послушайте, писать хорошие модульные тесты сложно. Мы все это знаем. Все в какой-то момент сталкиваются с этим. Но это не значит, что ваши посредственные тесты должны быть нечитаемыми. Вот 3 совета, которые помогут сделать эти средние тесты простыми, чистыми и точными.
1. Один тест, одна функция.
Не объединяйте несколько тестов в одну функцию, как это.
public void testBasicStuff() { Bar bar = makeBar(); Bar expectedBar = makeBar().setBuzzer(3); runBuzzProgram(bar); assertThat(bar).isEqualTo(expectedBar); Bar expectedBar2 = makeBar().setBuzzer(6); runBuzzProgram(bar); assertThat(bar).isEqualTo(expectedBar2); Bar expectedBarFinal = makeBar().setBuzzer(0); runBuzzShutdownProgram(bar); assertThat(bar).isEqualTo(expectedBarFinal); }
Сводить эти 3 теста в одну кучу неудачно, потому что:
- Это делает неоднозначным, влияет ли материал из первого теста на материал из второго и третьего тестов.
- Начальное состояние во втором и третьем тестах не является явным, поскольку это «какое бы состояние ни было после завершения первого теста». Об этом гораздо труднее рассуждать.
Чтобы исправить это, явно настройте начальное состояние для тестов №2 и №3 в новых функциях:
public void testOne() { Bar bar = makeBar(); Bar expectedBar = makeBar().setBuzzer(3); runBuzzProgram(bar); assertThat(bar).isEqualTo(expectedBar); } public void testTwo() { // Here, we set up the same state that test #1 ended in. Bar bar = makeBar().setValue(3); Bar expectedBar2 = makeBar().setBuzzer(6); runBuzzProgram(bar); assertThat(bar).isEqualTo(expectedBar2); } public void testThree() { // Here, we set up the same state that test #2 ended in. Bar bar = makeBar().setValue(6); Bar expectedBarFinal = makeBar().setBuzzer(0); runBuzzShutdownProgram(bar); assertThat(bar).isEqualTo(expectedBarFinal); }
Обратите внимание, как явное определение начального состояния упрощает анализ каждого теста, и чтобы прочитать любой из этих тестов, нам нужно всего лишь просмотреть 4 строки кода. Вы бы не прочли эту версию?
2. Хорошие тестовые имена.
public void testBasicFunctionality() {
Жалко видеть такие названия тестов, когда они могли выглядеть так:
public void testMyFunc_updatesBar_inExpectedGeneralCase() { ... } public void testMyFunc_updatesBar_whenFooServiceReturnsError() { ... }
Здесь мне даже не нужно читать реализацию теста, чтобы знать, что мы тестируем, потому что мы дали тесту удобное имя.
Лично мне нравится структурировать свои имена тестов следующим образом:
public void testSYSTEM_BEHAVIOR_CONDITION() {
Например: public void testAuthenticate_rejectsUser_whenBadCredentialsProvided() {
3. AAA: организовать, действовать, отстаивать
Организовать: настройте тест. Имитация исходящих RPC, заполнение поддельных баз данных и т. Д.
Действие: запуск тестируемой функции.
Утверждение: проверка ожидаемого поведения.
public void testMyFunc_updatesBar() { Bar bar; Bar expectedBar = makeBar().setBuzzer(3); myFunc(bar); assertThat(bar).isEqualTo(expectedBar); }
Этот код, несмотря на то, что он состоит из 4 строк, уже немного сбивает с толку, потому что AAA не разделены. Почему expectedBar
объявлен наверху? Будет ли он использоваться в myFunc()
? Когда заканчивается установка и когда фактически запускается тест?
Вы можете предотвратить всю эту путаницу, разделив эти 3 шага в своем коде:
public void testMyFunc_updatesBar() { Bar bar; myFunc(bar); Bar expectedBar = makeBar().setBuzzer(3); assertThat(bar).isEqualTo(expectedBar); }
Вот еще один пример, на этот раз на Python:
Мы не видели, что делает foo()
, или что supposeGarbleServiceReturns()
, или даже makeSimpleUserWithExistingBaz()
, но вы можете сразу понять, что здесь происходит, потому что тест назван хорошо, 3 этапа разделены, и он соответствующим образом прокомментирован.
Ни одна из этих трех стратегий не требует какого-либо серьезного рефакторинга или отладки для реализации, даже в существующем коде. И все же их влияние на удобочитаемость и ремонтопригодность больших наборов тестов может быть огромным. Пожалуйста, ради своих коллег и тех, кто когда-нибудь унаследует вашу кодовую базу, отформатируйте свои модульные тесты!
📝 Прочтите этот рассказ позже в Журнале.
🗞 Просыпайтесь каждое воскресное утро и слышите самые интересные истории, мнения и новости недели, ожидающие в вашем почтовом ящике: Получите примечательный информационный бюллетень›