Недавно клиент попросил экстренную версию какого-либо кода для отображения сообщения на экране, по юридическим причинам, на соответствующих страницах своего веб-сайта.
Сценарий состоял в том, что часть информации должна отображаться на экране, если она существует в базе данных — очень простой сценарий, который может быть покрыт несколькими простыми строками кода. Однако в спешке с написанием кода разработчик не писал никаких модульных тестов, и код содержал ошибку, которую не было обнаружено, пока патч не достиг UAT. Вы можете спросить, что это за ошибка, и это было то, что мы все сделали в какой-то момент нашей карьеры: добавив нежелательную точку с запятой ‘;’ до конца строки.
Я собираюсь продемонстрировать переписанную, двойную версию кода с использованием сценария AddressService, который я использовал в моих предыдущих блогах «Методы тестирования», как показано на диаграмме UML ниже:
В этой демонстрации функциональность изменилась, но логика и структура примера кода практически не изменились. В мире AddressService логика работает так:
- Получить адрес из базы данных.
- Если адрес существует, отформатируйте его и верните полученную строку.
- Если адрес не существует, вернуть ноль.
- Если форматирование не удалось, не беспокойтесь об этом и верните ноль.
Переписанный AddressService.findAddress (…) выглядит примерно так:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
@Componentpublic class AddressService { private static final Logger logger = LoggerFactory .getLogger(AddressService.class); private AddressDao addressDao; public String findAddressText(int id) { logger.info("In Address Service with id: " + id); Address address = addressDao.findAddress(id); String formattedAddress = null; if (address != null); try { formattedAddress = address.format(); } catch (AddressFormatException e) { // That's okay in this business case so ignore it } logger.info("Leaving Address Service with id: " + id); return formattedAddress; } @Autowired @Qualifier("addressDao") void setAddressDao(AddressDao addressDao) { this.addressDao = addressDao; }} |
Вы заметили ошибку? Я не сделал, когда я рассмотрел код … На всякий случай, я прокомментировал снимок экрана ниже:
Цель демонстрации тривиальной ошибки, простой ошибки, которую может сделать каждый, состоит в том, чтобы подчеркнуть важность написания нескольких модульных тестов, потому что модульные тесты выявили бы проблему и сэкономили бы массу времени и средств. Конечно, каждая организация отличается, но выпуск приведенного выше кода вызвал следующую последовательность событий:
- Приложение развернуто в Dev, Test и UAT.
- Команда тестировщиков проверила, что измененный экран работает нормально и передаст изменения.
- Другие экраны проходят регрессионное тестирование и признаны некорректными. Все неисправные экраны отмечены.
- Срочное сообщение об ошибке поднят.
- Отчет проходит через различные уровни управления.
- Сообщение передается мне (и я пропускаю обед), чтобы исследовать возможную проблему.
- Отчет направляется трем другим членам команды для расследования (четыре пары глаз лучше, чем одна)
- Обидная точка с запятой найдена и исправлена.
- Код повторно тестируется в dev и возвращается в систему контроля версий.
- Приложение создается и разворачивается в Dev, Test и UAT.
- Команда тестирования проверяет, что измененный экран работает нормально, и передает изменения.
- Другие экраны проходят регрессионное тестирование и проходят.
- Аварийное исправление прошло.
Вышеуказанная цепочка событий, очевидно, тратит значительное количество человеко-часов, стоит лишних денег, излишне повышает уровень стресса у людей и портит нашу репутацию с клиентом: все это очень веские причины для написания юнит-тестов.
Чтобы доказать это, я написал три недостающих модульных теста, и мне потребовалось всего лишь 15 минут времени на разработку, что по сравнению с количеством потраченных человеко-часов кажется хорошим использованием времени разработчика.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
@RunWith(UnitilsJUnit4TestClassRunner.class)public class WhyToTestAddressServiceTest { private AddressService instance; @Mock private AddressDao mockDao; @Mock private Address mockAddress; /** * @throws java.lang.Exception */ @Before public void setUp() throws Exception { instance = new AddressService(); instance.setAddressDao(mockDao); } /** * This test passes with the bug in the code * * Scenario: The Address object is found in the database and can return a * formatted address */ @Test public void testFindAddressText_Address_Found() throws AddressFormatException { final int id = 1; expect(mockDao.findAddress(id)).andReturn(mockAddress); expect(mockAddress.format()).andReturn("This is an address"); replay(); instance.findAddressText(id); verify(); } /** * This test fails with the bug in the code * * Scenario: The Address Object is not found and the method returns null */ @Test public void testFindAddressText_Address_Not_Found() throws AddressFormatException { final int id = 1; expect(mockDao.findAddress(id)).andReturn(null); replay(); instance.findAddressText(id); verify(); } /** * This test passes with the bug in the code * * Scenario: The Address Object is found but the data is incomplete and so a * null is returned. */ @Test public void testFindAddressText_Address_Found_But_Cant_Format() throws AddressFormatException { final int id = 1; expect(mockDao.findAddress(id)).andReturn(mockAddress); expect(mockAddress.format()).andThrow(new AddressFormatException()); replay(); instance.findAddressText(id); verify(); }} |
И, наконец, рискуя показаться самодовольным, я должен признаться, что, хотя в этом случае ошибка была не моей, в прошлом я выпускал подобные ошибки в дикую природу — до того, как научился писать модульные тесты…
Исходный код доступен на GitHub по адресу:
мерзавец: //github.com/roghughe/captaindebug.git
Ссылка: Почему вы должны писать модульные тесты — Методы тестирования 8 от нашего партнера JCG Роджера Хьюза в блоге Captain Debug .
Статьи по Теме :
- Методы тестирования — не писать тесты
- Неправильное использование сквозных тестов — Методы тестирования 2
- Что следует тестировать? — Методы испытаний 3
- Регулярные юнит-тесты и заглушки — Методы испытаний 4
- Модульное тестирование с использованием макетов — Методы тестирования 5
- Создание заглушек для устаревшего кода — методы тестирования 6
- Подробнее о создании заглушек для устаревшего кода — Методы тестирования 7
- Некоторые определения — методы тестирования 9

