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

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

📝 Прочтите этот рассказ позже в Журнале.

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