Как обрабатывать недостижимые исключения с TDD

79
8

Я все еще получаю голову вокруг частей TDD. У меня есть новая библиотека, которую я пишу, так что это кажется хорошей возможностью попробовать.

То, что я читал в TDD, рекламирует 100% -ный охват кода, но это кажется немного башней из слоновой кости, поэтому я сконфигурировал JaCoco для покрытия 90% кода, чтобы дать мне немного передышки.

Я начал с кода, загружающего KeyStore. Там много кодовых табличек и множество проверенных исключений. Поэтому начало здесь облегчает мою жизнь в будущем. Все выглядит хорошо, и мои тесты проходят. Но покрытие кода составляет всего 49%. Просматривая код, все покрывается за исключением того, что я бы назвал "Невозможные исключения", как этот:

public void saveKey(Key key, String alias) {
KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(new SecretKeySpec(key.getMaterial(), "AES"));

try {
keyStore.setEntry(alias, entry, new KeyStore.PasswordProtection(password));
} catch (KeyStoreException e) {
throw new UnexpectedErrorException("Failed to save the key", e);
}
}

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

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

В таких случаях, как TDD достигает мифического покрытия на 100%?

Я мог бы высмеять KeyStore, но совет от Mockito - "Не издевайтесь над типами, которыми вы не владеете". Поэтому я бы предпочел. Также KeyStore полагается на несколько статических методов, в которых Mockito не помогает, и я не хочу приводить PowerMock для простого случая, и я не уверен, что бросать больше библиотек в проблему является идеальным решением.

Так:

    Является ли покрытие TDD 100% кодом мифическим? Есть ли способ получить анализ кода, чтобы распознать этот код как охватываемый?

Мое ожидаемое решение на данный момент состоит в том, чтобы переместить мое настроенное ограничение на 90% охвата кода до 40 или 50 процентов, пока у меня не будет больше классов, чтобы охватить мой средний охват. Но прежде чем я это сделаю, есть что-то, что мне не хватает?

спросил(а) 2021-01-19T20:25:32+03:00 9 месяцев назад
1
Решение
102

Как и большинство аспектов программирования, тестирование требует задумчивости. TDD - очень полезный, но, конечно, недостаточный инструмент, который поможет вам получить хорошие тесты. Если вы тщательно и хорошо тестируете, я бы ожидал процент покрытия в верхних 80-х или 90-х годах. Я бы подозрительно относился к чему-то вроде 100% - пахнет кто-то, кто пишет тесты, чтобы сделать номера покрытия счастливыми, но не задумываясь о том, что они делают. - Мартин Фаулер, 2012

Для ядра вашей программы 100% охват может быть достижимой целью; в конце концов, вы никогда не вводите код без неудачного теста, который требуется, и "рефакторинг" не должен вводить неиспользуемые ветки.

Но код, который взаимодействует с границей... 100% -ый охват становится намного более дорогим, и вы в конечном итоге достигаете переломного момента:

Мне платят за код, который работает, а не за тесты, поэтому моя философия состоит в том, чтобы как можно меньше протестировать, чтобы достичь определенного уровня уверенности... - Кент Бек, 2008

Если KeyStoreException не были отмечены, вы не использовали бы этот конкретный пример; проблемы проверенных исключений несколько уникальны для Java.

Дэвид Парнас, писавший в 1972 году, дал нам несколько советов о том, как мы можем справиться с этой проблемой. Вы можете разработать свое решение, чтобы оно скрывало ваше решение использовать java.security.KeyStore. Другими словами, вы создаете интерфейс, описывающий API, который вы хотите сохранить в хранилище ключей, и напишите весь свой код на этот интерфейс. Только реализация должна знать подробные сведения об управлении исключениями; только ваша реализация должна знать, что вы решили, что исключения KeyStore не подлежат восстановлению.

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

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

Мое ожидаемое решение на данный момент состоит в том, чтобы переместить мое настроенное ограничение на 90% охвата кода до 40 или 50 процентов, пока у меня не будет больше классов, чтобы охватить мой средний охват.

Использование храпового механизма для предотвращения регрессии в вашей статистике покрытия - хорошая идея.

ответил(а) 2021-01-19T20:25:32+03:00 9 месяцев назад
47

То, что я читал в TDD, рекламирует 100% -ный охват кода.

Это распространенное заблуждение.

Когда мы делаем TDD, мы не нацелены на покрытие 100% кода. Мы нацелены на покрытие 100% потребностей. И нет: 100% -ый охват кода не подразумевает 100-процентное покрытие требований и наоборот...

К сожалению, мы не можем измерить охват требований. Таким образом, единственный способ получить это - это делать TDD.

В этом конкретном случае, согласно документам, KeyStoreException вызывается, если keyStore не был инициализирован. Я кодирую защиту и гарантирую, что keyStore будет инициализирован на этом этапе. Поэтому исключение KeyStoreException невозможно.

В таких случаях, как TDD достигает мифического покрытия на 100%?

Я мог бы высмеять KeyStore, но совет от Mockito - "Не издевайтесь над типами, которыми вы не владеете".

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

ответил(а) 2021-01-19T20:25:32+03:00 9 месяцев назад
46

На самом деле TDD не имеет никакого отношения к охвату кода.
Одним из правил TDD является

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

Поэтому, если вы практикуете Test-Driven Development и следуете правилу выше, вы всегда будете иметь 100% -ный охват кода.

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

ответил(а) 2021-01-19T20:25:32+03:00 9 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема