Как выполнить модульное тестирование кода, который является очень сложным за общедоступным интерфейсом - PullRequest
10 голосов
/ 06 января 2010

Мне интересно, как я должен тестировать такую ​​функциональность через NUnit.

Public void HighlyComplexCalculationOnAListOfHairyObjects()
{
    // calls 19 private methods totalling ~1000 lines code + comments + whitespace
}

Из чтения я вижу, что NUnit не предназначен для тестирования частных методов по философским соображениям о том, каким должно быть модульное тестирование; но попытка создать набор тестовых данных, которые полностью выполняли бы все функции, задействованные в вычислениях, была бы почти невозможна. Между тем, расчет разбит на несколько более мелких методов, которые являются достаточно дискретными. Однако это не то, что логично делать независимо друг от друга, поэтому все они определены как частные.

Ответы [ 9 ]

8 голосов
/ 06 января 2010

Вы объединили две вещи. Интерфейс (который может представлять очень мало) и этот конкретный класс реализации, который может предоставлять гораздо больше.

  1. Определить максимально узкий интерфейс.

  2. Определите класс реализации с помощью тестируемых (не закрытых) методов и атрибутов. Это нормально, если у класса есть "лишние" вещи.

  3. Все приложения должны использовать Интерфейс и, следовательно, не иметь безопасного доступа к доступным функциям класса.

Что если «кто-то» обходит Интерфейс и напрямую использует Класс? Они социопаты - их можно смело игнорировать. Не предоставляйте им поддержку по телефону, потому что они нарушили основное правило использования интерфейса, а не реализации.

3 голосов
/ 06 января 2010

Как уже упоминалось, InternalsVisibleTo("AssemblyName") - это хорошее место для тестирования устаревшего кода.

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

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

3 голосов
/ 06 января 2010

Чтобы решить вашу непосредственную проблему, вы можете взглянуть на Pex , который представляет собой инструмент от Microsoft Research, который решает эту проблему, находя все соответствующие граничные значения, чтобы все пути кода могли быть казненным.

Тем не менее, если бы вы использовали Test-Driven Development (TDD), вы бы никогда не оказались в такой ситуации, поскольку было бы почти невозможно написать модульные тесты, которые управляют этим видом API.

Метод, подобный тому, который вы описываете, звучит так, будто он пытается сделать слишком много вещей одновременно. Одним из ключевых преимуществ TDD является то, что он заставляет вас реализовывать свой код из небольших компонуемых объектов вместо больших классов с негибкими интерфейсами.

2 голосов
/ 06 января 2010

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

Одной из проблем такого рефакторинга является то, что в исходном классе было много состояний, в которых новый класс нуждался бы. Это еще один запах кода, который указывает, что состояние должно быть перемещено в объект значения.

2 голосов
/ 06 января 2010

Лично я бы сделал составные методы внутренними, применил InternalsVisibleTo и проверил бы разные биты.

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

1 голос
/ 06 января 2010

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

1 голос
/ 06 января 2010

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

Честно говоря, я бы посмотрел, нет ли способа как-нибудь разбить этот код на более управляемый раздел.

0 голосов
/ 06 января 2010

пытается создать набор тестовых данных который полностью выполнил все функциональность, участвующая в вычисление было бы почти невозможно

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

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

0 голосов
/ 06 января 2010

Ваш вопрос подразумевает, что существует множество путей выполнения во всей подсистеме. Первая идея, которая приходит в голову, это «рефакторинг». Даже если ваш API остается интерфейсом с одним методом, тестирование не должно быть «невозможным».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...