В новинку для поблочного тестирования, как записать большие тесты? [закрытый]

252
задан pixelastic 15 July 2010 в 08:26
поделиться

7 ответов

Мои тесты кажутся настолько тесно связанными с методом (тестирование всего кодового пути, ожидая, что некоторые внутренние методы будут вызываться несколько раз с определенными аргументами), что кажется, что если я когда-либо реорганизую метод, тесты не пройдут, даже если окончательное поведение метода не изменится.

Я думаю, вы делаете это неправильно.

Модульный тест должен:

  • проверить один метод
  • предоставить некоторые конкретные аргументы этому методу
  • проверить, что результат соответствует ожидаемому

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

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

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

testAdd()
{
    int x = 5;
    int y = -2;
    int expectedResult = 3;

    Calculator calculator = new Calculator();
    int actualResult = calculator.Add(x, y);
    Assert.AreEqual(expectedResult, actualResult);
}

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

175
ответ дан 23 November 2019 в 02:53
поделиться

Не пишите тесты, чтобы получить полное покрытие вашего кода. Пишите тесты, которые гарантируют выполнение ваших требований. Вы можете обнаружить ненужные кодовые пути. И наоборот, если они необходимы, то они нужны для выполнения какого-то требования; выясните его и тестируйте требование (а не путь).

Делайте тесты небольшими: один тест на одно требование.

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

14
ответ дан 23 November 2019 в 02:53
поделиться

Что касается модульного тестирования, я обнаружил, что как Test Driven (сначала тесты, затем код), так и сначала код, а затем второй тест, которые были чрезвычайно полезны.

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

Тем не менее, тестирование похоже на двойное кодирование. На самом деле у меня были приложения, в которых тестового кода (количества) было больше, чем кода приложения. Одним из примеров был очень сложный конечный автомат.Я должен был убедиться, что после добавления к нему дополнительной логики, все это всегда работало во всех предыдущих вариантах использования. И поскольку эти случаи было довольно сложно отследить, глядя на код, у меня был такой хороший набор тестов для этой машины, что я был уверен, что он не сломается даже после внесения изменений, и тесты несколько раз спасли мою задницу. . И поскольку пользователи или тестировщики находили ошибки в потоке или неучтенные случаи, угадайте, что, добавляли в тесты и никогда больше не повторялись. Это действительно придало пользователям уверенности в моей работе, а также сделало все это очень стабильным. А когда его пришлось переписать из соображений производительности, угадайте, что, благодаря тестам он работал, как ожидалось, на всех входах.

Все простые примеры, такие как function square (number) , хороши и все такое, и, вероятно, плохие кандидаты для того, чтобы тратить много времени на тестирование. Те, которые выполняют важную бизнес-логику, вот где важно тестирование. Проверьте требования. Не проверяйте только сантехнику. Если требования изменятся, то угадайте, что, тесты тоже должны.

Тестирование не должно быть буквально тестированием того, что функция foo вызывала функциональную панель 3 раза. Это не правильно. Проверьте правильность результата и побочных эффектов, а не внутреннюю механику.

30
ответ дан 23 November 2019 в 02:53
поделиться

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

When I'm writing tests for a method, I have the feeling of rewriting a second time what I          
already wrote in the method itself.
My tests just seems so tightly bound to the method (testing all codepath, expecting some    
inner methods to be called a number of times, with certain arguments), that it seems that
if I ever refactor the method, the tests will fail even if the final behavior of the   
method did not change.

Это потому, что вы пишете свои тесты после того, как написали свой код. Если бы вы сделали наоборот (написали тесты первыми), этого бы не было.

4
ответ дан 23 November 2019 в 02:53
поделиться

Юнит-тестирование - это вывод, который вы получаете от функции/метода/приложения. Совершенно неважно, как получен результат, важно только, чтобы он был правильным. Поэтому ваш подход к подсчету вызовов внутренних методов и тому подобное неверен. Я обычно делаю так: сажусь и пишу, что должен вернуть метод при определенных входных значениях или определенном окружении, затем пишу тест, который сравнивает фактическое возвращаемое значение с тем, что я придумал.

13
ответ дан 23 November 2019 в 02:53
поделиться

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

Перемещение существующего кода в Test Driven Development

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

18
ответ дан 23 November 2019 в 02:53
поделиться

Попробуйте написать юнит-тест до написания метода, который он будет тестировать.

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

Вы всегда должны тестировать результаты метода, а не то, как метод получает эти результаты.

8
ответ дан 23 November 2019 в 02:53
поделиться
Другие вопросы по тегам:

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