Нужны идеи для подхода TDD - PullRequest
2 голосов
/ 04 декабря 2009

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

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

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

Для краткого объяснения менеджер загрузки содержит ряд «выгруженных» деталей, иногда сотнями, которые затем сбрасываются в созданные пользователем нагрузки и пулы повторных загрузок. В процессе создания и заполнения этих загрузок происходит множество удалений, изменений и дополнений, которые в конечном итоге вызывают проблемы. Однако, потому что, когда вы макете метод объекта, побеждает последний макет, то есть:

jobDetailControllerMock.Setup(mock => mock.GetById(1)).Returns(jobDetail1);
jobDetailControllerMock.Setup(mock => mock.GetById(2)).Returns(jobDetail2);
jobDetailControllerMock.Setup(mock => mock.GetById(3)).Returns(jobDetail3);

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

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

TL / DR: я в основном ищу стратегии тестирования для ориентированного на данные кода, который довольно сложен по своей природе.

Ответы [ 6 ]

2 голосов
/ 04 декабря 2009

Как отметил Себ, вы действительно можете использовать соответствие диапазона:

controller.Setup(x => x.GetById(It.IsInRange<int>(1, 3, Range.Inclusive))))).Returns<int>(i => jobs[i]);

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

1 голос
/ 11 декабря 2009

Похоже, что LoaderManagerHandler делает ... довольно много работы. «Менеджер» в имени класса всегда меня несколько беспокоит ... с точки зрения TDD, возможно, стоит подумать о том, чтобы разделить класс соответствующим образом, если это возможно.

Как долго этот класс?

1 голос
/ 04 декабря 2009

Чтобы обойти "последние пробные победы" с Moq, вы можете использовать технику из этого блога:

Moq Triqs - Последовательные ожидания

EDIT:

На самом деле вам это даже не нужно. На основе вашего примера Moq будет возвращать разные значения в зависимости от аргумента метода.

public interface IController { string GetById(int id); }</p> <pre><code>class Program { static void Main(string[] args) { var mockController = new Mock<IController>(); mockController.Setup(x => x.GetById(1)).Returns("one"); mockController.Setup(x => x.GetById(2)).Returns("two"); mockController.Setup(x => x.GetById(3)).Returns("three"); IController controller = mockController.Object; Console.WriteLine(controller.GetById(1)); Console.WriteLine(controller.GetById(3)); Console.WriteLine(controller.GetById(2)); Console.WriteLine(controller.GetById(3)); Console.WriteLine(controller.GetById(99) == null); } }

Вывод:

   one
   three
   two
   three
   True
0 голосов
/ 04 декабря 2009

Независимо от того, что я отправляю на jobDetailController.GetById (x), я всегда буду возвращаться на jobDetail3

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

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

0 голосов
/ 04 декабря 2009

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

0 голосов
/ 04 декабря 2009

Я никогда не использовал Moq, но, похоже, он должен соответствовать ложному вызову по предоставленным аргументам.

Быстрый просмотр документации Quick Start содержит следующую выдержку:

//Matching Arguments

// any value
mock.Setup(foo => foo.Execute(It.IsAny<string>())).Returns(true);


// matching Func<int>, lazy evaluated
mock.Setup(foo => foo.Add(It.Is<int>(i => i % 2 == 0))).Returns(true); 


// matching ranges
mock.Setup(foo => foo.Add(It.IsInRange<int>(0, 10, Range.Inclusive))).Returns(true); 

Я думаю, что вы должны использовать второй пример выше.

...