2011-01-19 1 views
4

Давайте сделаем вид, что у меня есть 3 класса, каждый из которых имеет один ответственно (и метод). Давайте также притворимся, что эти классы имеют интерфейсы для облегчения инъекции зависимостей. Класс A вызывает интерфейс для класса B, а класс B вызывает интерфейс для класса C. Если класс C выдает исключение NotAPrimeNumberException (если, скажем, параметр int не является простым числом), я бы ожидал, что будет единый тест, который гарантирует, что что C выдает исключение NotAPrimeNumberException, если переданный параметр не является простым числом. Все идет нормально.Единичное тестирование нескольких уровней исключений - где остановиться?

В настоящее время я убежден, что модульные тесты предоставляют всю документацию, необходимую для понимания поведения тестируемого метода. Таким образом, вышеупомянутый модульный тест был бы чем-то вроде MakeSureNotAPrimeNumberExceptionIsThrownIfNumberisNotPrimeTest().

Класс B знает, что класс C может вызывать исключение NotAPrimeNumberException. Если я хочу, чтобы исключение NotAPrimeNumberException вышло из класса B, я должен написать модульный тест для класса B, чтобы каким-то образом проверить, что в некоторых случаях возникает исключение NotAPrimeNumberException? Что относительно Класса А? Если A также позволяет пузырьку NotAPrimeNumberException, если у него также есть единичный тест для этого?

Мое беспокойство заключается в том, что если я не буду писать модульный тест в классе B, то потребители для класса B не будут знать, что класс B может вызывать этот тип исключения. Однако, если я напишу модульный тест, то немного глупо, что мне нужно заставить вызов класса B выбросить исключение NotAPrimeNumberException, чтобы НЕ обрабатывать исключение в классе B. Если я не буду писать единичный тест, что является ли подходящий способ, если таковой имеется, документировать в API, что это исключение может возникнуть?

Бонусный вопрос: как вы можете это сделать в NUnit с Rhino-mocks? Это, конечно, зависит от первых двух вопросов.

+1

Тест должен проходить, когда ожидается ожидаемое поведение. Даже если это поведение бросает исключение. –

ответ

2

Если классы A и B ожидали определенного поведения из классов, с которыми сотрудничают, тогда вы должны протестировать, чтобы обеспечить такое поведение. Если вы хотите избежать огромного шара грязи, к которому вы направляетесь, имея жесткие звонки от A до B до C, вы должны применить Голливудский принцип/DIP, чтобы нарушить эту зависимость. A должен зависеть от IB (интерфейс B), а не от экземпляра B. Тогда тесты A могут просто утверждать, что A делает то, что он должен делать, в том числе действуя должным образом перед исключениями, брошенными IB (или игнорируя их) ,Аналогично, B может зависеть от интерфейса C, IC и тестов B, может аналогичным образом игнорировать детали того, что C может или не может делать, вместо этого фокусируясь только на том, что делает B.

Интеграционный тест, который соединяет А-В от В до С, сможет проверить правильность взаимодействия классов, а также обеспечить, чтобы исключение, поднятое в С, пузырилось от А, если это то, что вы хотите, чтобы это произошло. И если позже выяснится, что C просто возвращает null, а не создает исключение, тогда ваш тест интеграции завершится неудачно, и вы увидите, что произошло новое поведение. Тем не менее, я бы не стал загромождать свои индивидуальные тесты с помощью тестов, показывающих, что если ситуация нарушается, исключение будет пузыриться, поскольку это поведение по умолчанию. Вместо этого я могу проверить, что любые новые исключения, которые я поднимаю, бросаются, когда ожидается, и что любая обработка исключений, которые я выполняю, работает, как я ожидаю. Это связано с тем, что в модульном тесте вы должны тестировать только тестируемый код, а не поведение соавторов.

+0

Извините, я только что обновил комментарий, чтобы быть немного понятнее. Я просто предполагал, что есть интерфейсы и инъекции зависимостей для насмешливых целей, но в этом вопросе не сказано. Реальная точка моего вопроса заключается в написании модульного теста для метода, потому что зависимость генерирует исключение и имеет ли значение, чтобы гарантировать, что мои модульные тесты документируют все поведение моего метода. – jakejgordon

+0

Вы ответили на ваш вопрос своим комментарием. В ваших модульных тестах должно быть указано «все поведение моего метода» и * не * поведение методов, которые может вызвать ваш метод. Пока вы это делаете, вы в порядке. Если важно, обрабатывает ли ваш метод исключения, связанные с ним, легко (с помощью макетов или подделок) настраивать тест, в котором исключение передается вашему методу и проверять его с ним. Если это важное поведение, это кажется действительным для меня как единичный тест. Но издевайтесь над взаимодействием, не полагайтесь на конкретную реализацию другого метода. – ssmith

+0

Я считаю, что для потребителя класса B важно знать, что вы можете получить исключение NotAPrimeNumberException в зависимости от значения, которое вы передаете для одного из параметров.Считаете ли вы, что использование модульных тестов для документации является достаточно хорошей причиной для тестирования класса B, который называется чем-то вроде ThrowsNotAPrimeNumberExceptionIfNotAPrimeNumberTest()? Тест просто высмеивал бы исключение, исходящее из класса C, и убедитесь, что класс B выбрасывает одно и то же исключение. – jakejgordon

1

Вы должны использовать насмешку над B и C и обращаться к их методам. Это точка теста UNIT - вам нужно всего лишь протестировать блок кода, а не его зависимости.

Если вы не хотите использовать насмешку в этом случае, я думаю, что не должно быть особых условий, позволяющих ему проходить, если исключение выбрано в одной из зависимостей. Это должно потерпеть неудачу. Первая причина: точный код, который вы тестируете, фактически не работает (вы тестируете его в целом, с зависимостями). Вторая причина: ваши тесты становятся беспорядочными.

Не забудьте сохранить его простым.

+0

Извините, я только что обновил комментарий, чтобы быть немного понятнее. Я просто предполагал, что есть интерфейсы и инъекции зависимостей для насмешливых целей, но в этом вопросе не сказано. Реальная точка моего вопроса заключается в написании модульного теста для метода, потому что исключение вызывает зависимость, и имеет ли смысл обеспечить, чтобы мои модульные тесты документировали все поведение моего метода. – jakejgordon

0

Если классам A и B действительно не нужно заботиться о том, существует ли исключение, то вы не должны тестировать эту функциональность. Если у них есть какое-то конкретное поведение в этом исключении (возможно, они переносят это в другое исключение или ловят его и громко жалуются на звуковые сигналы), и вам нужно проверить это поведение, тогда вам нужно.

В первом случае я бы принял решение не тестировать единицы измерения A и B, чтобы избежать хрупкости моих тестов. Если не важно, что вы проверяете поведение A и B перед лицом определенного исключения, то вы не хотите связывать эти тесты с поведением другого модуля.

2

Вот некоторые мысли:

  1. Класс C разработан, чтобы бросить NotAPrimeNumberException. Следовательно, вы должны иметь тест, который проверяет, что исключение генерируется при ожидании.
  2. Независимо от того, тестируете ли вы класс B или A, в этом случае зависит от того, делают ли они что-либо для обработки исключения. Если классу B необходимо выполнить какое-либо действие, когда класс C выбрасывает исключение, вы должны проверить его. Если класс B просто позволяет этому исключению «пройти», тогда вы не должны его проверять. В противном случае вам также необходимо будет протестировать поведение NullReferenceException, ArithmeticOverflowException, HttpException и всех других типов исключений. Это модульное тестирование ad nausaeam и не полезно.
  3. Что касается изменения поведения класса C, у вас есть несколько вещей, которые следует учитывать. Любой тест, который зависит от измененного поведения, также должен быть изменен. (Поскольку вы делаете TDD, вы изменили тесты до того, как изменили поведение, не так ли?) Кроме того, также необходимо будет изменить любые макеты, которые эмулируют поведение исключения исключений класса C. Нет смысла проверять, что происходит, когда класс C генерирует исключение, если оно никогда не выбрасывает это исключение. Это последнее рассмотрение несколько проблематично, и я не знаю, как его можно управлять формально. Меня интересуют дальнейшие мысли.
+0

Извините, я только что обновил комментарий, чтобы быть немного понятнее. Я просто предполагал, что есть интерфейсы и инъекции зависимостей для насмешливых целей, но в этом вопросе не сказано. Реальная точка моего вопроса заключается в написании модульного теста для метода, потому что исключение вызывает зависимость, и имеет ли смысл обеспечить, чтобы мои модульные тесты документировали все поведение моего метода. Я удалил вопрос, на который вы ответили пулевым номером 3, потому что вопрос был не очень хорошим :( – jakejgordon