Как бы я пошел о модульном тестировании этого? - PullRequest
5 голосов
/ 08 декабря 2010

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

Общее описание: пользователь должен иметь возможность удалить план. У плана есть связанные с ним задачи, их также необходимо удалить (если они еще не выполнены).

Псевдокод, как должен вести себя алгоритм:

   PlanController.DeletePlan(plan)
     =>
     PlanDbRepository.DeletePlan()
      ForEach Task t in plan.Tasks
          If t.Status = Status.Open Then
            TaskDbRepository.DeleteTask(t)
          End If
      End ForEach

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

1) Смоделируйте вызовы Репозитория и проверьте, были ли они вызваны соответствующее количество раз как Утверждения

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

В обоих подходах главный вопрос: что именно я здесь тестирую? Какова ДОПОЛНИТЕЛЬНАЯ ценность, которую дали бы мне такие тесты?

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

Ответы [ 7 ]

4 голосов
/ 08 декабря 2010

Вы должны смоделировать хранилище, а затем создать фиктивный план в модульном тесте, содержащем как открытые, так и закрытые задачи.Затем вызовите реальный метод, передающий этот план, и в конце убедитесь, что метод DeleteTask был вызван с правильными аргументами (задачи только с status = Open).Таким образом, вы гарантируете, что ваш метод удалит только открытые задачи, связанные с этим планом.Также не забудьте (вероятно, в отдельном модульном тесте) проверить, что сам план был удален, утверждая, что метод DeletePlan был вызван для объекта, который вы передаете.

2 голосов
/ 08 декабря 2010

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

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

Некоторые тесты, которые вы могли бы написать:
Вызывает ли удаление пустого плана только DeletePlan?
Вызывает ли удаление плана с двумя открытыми задачами DeleteTask для обеих задач?
Удаление планас двумя закрытыми задачами вообще не вызывать DeleteTask?
Удаляет ли план с одной открытой и одной закрытой задачей DeleteTask один раз для правильной задачи?

Редактировать: я бы использовал ответ Даринакак способ идти об этом, хотя.

2 голосов
/ 08 декабря 2010

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

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

2 голосов
/ 08 декабря 2010

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

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

1 голос
/ 23 апреля 2013

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

Если у меня есть план с 3 заданиями:

Plan1 {
 Task1: completed
 Task2: todo
 Task3: todo
}

и я призываю удалить их, что должно произойти с Планом?

Plan1 : ?
Task1: not deleted
Task2: deleted
Task3: deleted

План1 удален, потерянная задача1? или он помечен как удаленный?

Это большая часть значения, которое я вижу в модульных тестах (хотя это только 1 из 4 значений: 1) Спец 2) Обратная связь 3) Регрессия 4) зернистость

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

public void DeletePlan(Plan p)
{ 
  var objectsToDelete = GetDeletedPlanObjects(p);
  DeleteObjects(objectsToDelete);
} 

И я бы не стал проверять этот метод. Я бы протестировал метод GetDeletedPlanObjects, который в любом случае не коснулся бы базы данных и позволил бы вам отправлять в сценариях, подобных описанной выше ситуации .... что я бы потом утверждал на www.approvaltests.com, но это другая история: )

Счастливого тестирования, Ллевеллин

0 голосов
/ 08 декабря 2010

IMO, вы можете написать свои юнит-тесты вокруг абстрактного PlanRepository, и те же самые тесты должны быть полезны и для проверки целостности данных в базе данных.

Например, вы можете написать тест -

void DeletePlanTest()
{
    PlanRepository repo = new PlanDbRepository("connection string");
    repo.CreateNewPlan(); // create plan and populate with tasks
    AssertIsTrue(repo.Plan.OpenTasks.Count == 2); // check tasks are in open state
    repo.DeletePlan();
    AssertIsTrue(repo.Plan.OpenTasks.Count == 0);
}

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

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

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

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

0 голосов
/ 08 декабря 2010

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

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

...