mspec и rhino имитируют тестирование ожидаемых исключений

#unit-testing #tdd #rhino-mocks #mspec

#модульное тестирование #tdd #rhino-издевается #mspec

Вопрос:

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

У меня есть метод контроллера (псевдокод):

 public ActionResult Register(formModel model)
{

    if (ModelState.isValid) {

        try {

            _userService.CreateUser(a bunch of parameters here);
            return RedirectToAction(some other action);
        }
        catch (Exception e)
        {

            ModelState.AddModelError("",e.Message);

        }

    }

    return View();
}
  

У меня есть куча отдельных тестов против «_userService». Метод «CreateUser» просто создает нового пользователя и ничего не возвращает или выдает исключение, если произошла ошибка (например, пользователь существует), которую я выводю на окружающий контроллер в try catch и добавляю исключение в ModelState.

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

В чем я не уверен, так это в том, как проверить, что, когда пользовательский сервис выдает ошибку, он не должен перенаправлять и должен добавлять это исключение в modelstate. С помощью rhino mocks вы можете заглушить макет, но в книге «Искусство модульного тестирования» этого не рекомендуется.

Прямо сейчас в моем тесте я вручную добавляю ошибку модели (не заботясь о том, что это из пользовательского сервиса) и проверяю, что контроллер возвращает тот же вид, если есть ошибки. Это правильный способ решения этой проблемы? Или, может быть, мне следует создать отдельный тест, в котором я заглушаю _userService, чтобы выдать ошибку и проверить, добавляется ли она в modelstate? Или я даже не должен проверять этот случай? Я чувствую, что, возможно, я просто переоцениваю все это, и тестирования с использованием modelstate было бы достаточно, чтобы удовлетворить это…

Ответ №1:

Ваш макет представляет сотрудничающий класс. Я бы не стал слишком зацикливаться на разнице между mocks и заглушками; это все еще сотрудничающий класс.

Вы можете представить свои модульные тесты как описание того, как использовать ваш класс, и как класс затем взаимодействует со своими сотрудниками. У вас есть два примера:

 Given a controller
When I register the model
Then the class should ask the user service to create a user.
  

И:

 Given a controller
Given the user service is broken
When I register the model
Then the class should attach the error to the model state.
  

Именно эта секунда Given говорит вам, что вы заглушаете, а не издеваетесь. Вы настраиваете пользовательскую службу так, как будто она сломана. Контекст, в котором действует класс, отличается, поэтому вам нужно заглушку, и вы действительно должны создать исключение.

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

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