2009-12-04 4 views
2

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

Гитары Load Manager находятся в классе 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 (х) я всегда вернусь jobDetail3. Это делает тестирование почти невозможным, потому что мы должны убедиться, что при внесении изменений все точки затронуты, что должно быть затронуто.

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

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

ответ

1

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

Moq Triqs - Successive Expectations

EDIT:

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

public interface IController { string GetById(int id); }

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

Я поддержал ваш ответ, потому что я не знал, что это возможно, но я не верю, что полностью добьется результатов, которые я ищу. Мне нужно вернуть конкретные данные на основе используемого аргумента, и этот метод не обязательно будет вызываться в заданном порядке. Большое вам спасибо, это поможет мне в другом месте. – joshlrogers

+0

Хорошо ... любые мысли о том, почему это не работает для меня тогда? Я использую последнюю версию Moq и независимо от того, какой аргумент я передаю, я получаю последний результат издевательства. – joshlrogers

+0

@josh - Не знаю. Я протестировал его с v3.1.416.3, который является последней не-бета-версией с сайта. Это стандартное поведение, поэтому синтаксис It.IsAny был введен для переопределения его, когда это необходимо. Я бы посоветовал написать простейший тест с вашим контроллером и посмотреть, можете ли вы устранить переменные. Вы также можете публиковать сообщения в Moq Discussionions - они очень полезны. http://groups.google.com/group/moqdisc – TrueWill

0

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

Беглый взгляд на Quick Start documentation имеет следующий отрывок:

//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); 

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

+0

Благодарим за отзыв. Я пробовал несколько разных способов, но в конечном итоге это то, что я нашел (в разделе замечаний): http://www.clariusconsulting.net/labs/moq/html/3BFDD309.htm – joshlrogers

+0

@josh - wow, эта ссылка вводит в заблуждение. Я отправлю отзыв автору об этом. – TrueWill

+0

@josh - Я думаю, что ссылка, на которую вы смотрите, действительно говорит «если есть два ожидания, которые соответствуют последней победе». В вашем случае, если вы используете совпадение It.Is, тогда конфликта не будет. –

0

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

0

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

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

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

2

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

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

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

1

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

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

+0

LoadManagerHandler выполняет совсем немного работы, теперь он включает в себя всю логику для модуля Load Manager (имя модуля является единственной причиной, почему «менеджер» включен в имя класса). Создание этого класса было попыткой удалить как охватывающее всю логику этого модуля в одном месте. Это примерно 1400 строк кода, поэтому не behemoth, и многие из них являются аргументацией и т. Д. Весь код доступа к данным обрабатывается уровнем данных. Из того, что я прочитал, это должен быть оптимальный способ сделать TDD, но потом я мог ошибаться. – joshlrogers

+0

Лично я считаю, что 1400 строк будут очень большим классом, который, вероятно, нужно будет реорганизовать на меньший размер, хотя и не патологически огромным. Более подробно, чем просто размер, описание класса заставило его звучать так, как будто оно имело ряд обязанностей, нарушая «принцип единой ответственности» и став классом Бога. Во всяком случае, когда я слышу, что «класс слишком сложный со слишком большим количеством зависимостей для тестирования», моя немедленная реакция всегда «разбивает его на более мелкие, более простые классы». Обращение шаблона COntrol может помочь. – kyoryu

Смежные вопросы