Проблема, которую вы описываете, очень реальна, и с ней очень легко столкнуться при работе с TDD. В целом вы можете сказать, что проблема заключается не в тестировании случайного поведения, а в том, что от этого случайного поведения зависят тонны тестов.
Принцип DRY применяется как к тестовому коду, так и к рабочему коду. Это часто может быть хорошим ориентиром при написании тестового кода. Цель должна состоять в том, чтобы все «случайные» действия, которые вы указываете на этом пути, были изолированными, чтобы их использовали только несколько тестов из всего набора тестов. Таким образом, если вам нужно реорганизовать это поведение, вам нужно изменить только несколько тестов вместо большой части всего набора тестов.
Лучше всего это достигается с помощью обильного использования интерфейсов или абстрактных классов в качестве коллабораторов, поскольку это означает, что вы получаете низкоуровневую связь.
Вот пример того, что я имею в виду. Предположим, что у вас есть какая-то реализация MVC, где Контроллер должен возвращать View. Предположим, что у нас есть такой метод на BookController:
public View DisplayBookDetails(int bookId)
Реализация должна использовать вставленный IBookRepository, чтобы получить книгу из базы данных, а затем преобразовать ее в представление этой книги. Вы можете написать множество тестов, чтобы охватить все аспекты метода DisplayBookDetails, но вы также можете сделать что-то еще:
Определите дополнительный интерфейс IBookMapper и добавьте его в BookController в дополнение к IBookRepository. Реализация метода может быть примерно такой:
public View DisplayBookDetails(int bookId)
{
return this.mapper.Map(this.repository.GetBook(bookId);
}
Очевидно, что это слишком упрощенный пример, но суть в том, что теперь вы можете написать один набор тестов для вашей реальной реализации IBookMapper, что означает, что при тестировании метода DisplayBookDetails вы можете просто использовать заглушку (лучше всего сгенерированную динамический фиктивный каркас) для реализации сопоставления, вместо того, чтобы пытаться определить хрупкие и сложные отношения между объектом «Домен книги» и тем, как он отображается.
Использование IBookMaper, безусловно, является случайной деталью реализации, но если вы используете SUT Factory или, что еще лучше, контейнер для автоматической имитации, определение этого случайного поведения является изолированным, что означает, что вы, если позже вы решите провести рефакторинг реализации, вы можете сделать это, только изменив тестовый код в нескольких местах.