Как к коду модульного теста, который очень сложен позади открытого интерфейса

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

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

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

10
задан Espo 4 February 2010 в 09:21
поделиться

9 ответов

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

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

  2. Определите класс Implementation с тестируемыми (нечастными) методами и атрибутами. Ничего страшного, если в классе есть "лишние" вещи.

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

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

8
ответ дан 3 December 2019 в 19:33
поделиться

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

Внутренние методы все еще являются закрытыми в том смысле, что сборки вне текущей сборки не видят методов. Проверьте MSDN на большую информативность.

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

.
3
ответ дан 3 December 2019 в 19:33
поделиться

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

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

.
2
ответ дан 3 December 2019 в 19:33
поделиться

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

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

.
2
ответ дан 3 December 2019 в 19:33
поделиться

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

.
1
ответ дан 3 December 2019 в 19:33
поделиться

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

0
ответ дан 3 December 2019 в 19:33
поделиться

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

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

1
ответ дан 3 December 2019 в 19:33
поделиться

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

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

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

3
ответ дан 3 December 2019 в 19:33
поделиться

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

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

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

0
ответ дан 3 December 2019 в 19:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: