Модульный тест метод, который находится на высоком уровне абстракции - PullRequest
3 голосов
/ 02 октября 2011

Подобная тема обсуждалась в Значение модульных тестов высокого уровня и фиктивных объектов

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

Я занимаюсь разработкой обычного 3-уровневого приложения, в котором используется Entity Framework.Выше EF у меня есть два слоя:

  • Репозитории : они напрямую обращаются к EF ObjectContext и выполняют всю работу CRUD (фактически, эти классы генерируются с помощью шаблона T4).Все классы репозитория имеют соответствующий интерфейс.
  • Менеджеры : они реализуют бизнес-логику более высокого уровня, они не обращаются напрямую к ObjectContext, а используют соответствующий репозиторий.Менеджеры не знают конкретной реализации Repository, только интерфейс (я использую внедрение зависимостей и макеты в модульном тесте).

Без дальнейшего описания, вот класс, который я хотел бы написатьмодульные тесты для:

public class PersonManager
{
    private IPersonRepository personRepository; // This is injected.

    // Constructor for injection is here.

    public void ComplexMethod()
    {
        // High level business logic
        bool result = this.SimpleMethod1();
        if(result)
            this.SimpleMethod2(1);
        else
            this.SimpleMethod2(2);
    }

    public bool SimpleMethod1()
    {
        // Doing some low-level work with the repository.
    }

    public void SimpleMethod2(int param)
    {
        // Doing some low-level work with the repository.
    }
}

Очень просто провести модульное тестирование SimpleMethod1 и SimpleMethod2, создав экземпляр PersonManager с помощью макета PersonRepository.

Но яНе могу найти удобный способ для юнит-теста ComplexMethod.

Есть ли у вас какие-либо рекомендации о том, как мне это сделать?Или это не должно быть проверено модулем вообще?Может быть, мне не следует использовать ссылку this для вызовов методов в ComplexMethod, а получить доступ к самому PersonManager через интерфейс и заменить его на макет тоже?

Заранее спасибо за любой совет.

Ответы [ 2 ]

6 голосов
/ 03 октября 2011

Хороший ответ Гийома (+1), но я хотел дать дополнительное наблюдение.То, что я вижу в опубликованном вами коде, является основанием для очень распространенного вопроса от людей, пытающихся выяснить (или спорить с) TDD, а именно:

"Как / почему я должен тестировать ComplexMethod ()так как это зависит от SimpleMethod1 () и SimpleMethod2 (), которые уже протестированы и имеют свое собственное поведение, которое я должен учитывать в тестах ComplexMethod ()? Мне пришлось бы в основном дублировать все тесты SimpleMethod1 ()и SimpleMethod2 () для того, чтобы полностью протестировать ComplexMethod (), и это просто глупо. "

Вскоре после этого они обычно узнают о частичных имитациях.Используя частичные имитации, вы можете имитировать SimpleMethod1 () и SimpleMethod2 (), а затем тестировать ComplexMethod (), используя обычные механизмы имитации.«Звучит отлично, - думают они, - это отлично решит мою проблему!».Хорошая фреймворковая среда должна настоятельно не рекомендовать использовать частичные макеты таким образом, потому что реальность такова:

Ваши тесты говорят вам о проблеме проектирования.

Специальноони говорят вам, что вы смешали проблемы и / или уровни абстракции в одном классе.Они говорят вам, что SimpleMethod1 () и SimpleMethod2 () должны быть извлечены в другой класс, от которого зависит этот класс.Независимо от того, сколько раз я вижу этот сценарий, и как бы яростно не утверждал разработчик, тесты подтверждаются в конце 100% времени.

4 голосов
/ 02 октября 2011

Я не вижу, в чем проблема. Вы можете проверить свой сложный метод во время проверки хранилища, проблем нет.

Вам потребуются два модульных теста, каждый из которых будет использовать ту же последовательность ожиданий и выполнений, что и в ваших тестах SimpleMethod1 (я предполагаю, что у вас уже есть два модульных теста для SimpleMethod1, один для возврата " true ", один для" false "), а также те же ожидания, что и у вас для теста SimpleMethod2 с фиксированным параметром 1 или 2 соответственно.

Конечно, в вашем классе тестирования будет какое-то "дублирование", но это не проблема.

Также обратите внимание, что ваши тесты для SimpleMethod2 не должны делать какие-либо предположения для переданного параметра: в «реальной жизни» вы можете иметь только 1 или 2 в качестве параметра (и это то, что должно быть у вашего юнит-теста для ComplexMethod), но ваши юнит-тесты для SImpleMethod2 должны проверять его независимо от того, какой это параметр: любой int.

И, наконец, если ComplexMethod является ЕДИНСТВЕННЫМ способом вызова SimpleMethod1 и / или SimpleMethod2, вам следует рассмотреть возможность сделать их закрытыми и иметь только модульные тесты для ComplexMethod.

Имеет ли это смысл?

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