Послушайте, писать хорошие модульные тесты сложно. Мы все это знаем. Все в какой-то момент сталкиваются с этим. Но это не значит, что ваши посредственные тесты должны быть нечитаемыми. Вот 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 этапа разделены, и он соответствующим образом прокомментирован.
Ни одна из этих трех стратегий не требует какого-либо серьезного рефакторинга или отладки для реализации, даже в существующем коде. И все же их влияние на удобочитаемость и ремонтопригодность больших наборов тестов может быть огромным. Пожалуйста, ради своих коллег и тех, кто когда-нибудь унаследует вашу кодовую базу, отформатируйте свои модульные тесты!

📝 Прочтите этот рассказ позже в Журнале.
🗞 Просыпайтесь каждое воскресное утро и слышите самые интересные истории, мнения и новости недели, ожидающие в вашем почтовом ящике: Получите примечательный информационный бюллетень›