Как справиться с настройкой сложных юнит-тестов и заставить их только тестировать юнит - PullRequest
5 голосов
/ 02 сентября 2010

У меня есть метод, который принимает 5 параметров. Этот метод используется для сбора собранной информации и отправки ее на мой сервер.

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

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

Вот гипотетический пример, чтобы попытаться прояснить ситуацию:

Файл: UserDemographics.cs

class UserDemographics
{
     // A bunch of user demographic here
     // and values that get set as a user gets added to a group.
}

Файл: UserGroups.cs

class UserGroups
{
     // A bunch of variables that change based on 
     //  the demographics of the users put into them.
     public AddUserDemographicsToGroup(UserDemographcis userDemographics)
     {}
}

Файл: UserSetupEvent.cs

class UserSetupEvent
{
     // An event to record the registering of a user
     // Is highly dependant on UserDemographics and semi dependant on UserGroups
     public SetupUserEvent(List<UserDemographics> userDemographics, 
                           List<UserGroup> userGroups)
     {}
}

файл: Communications.cs

class Communications
{
     public SendUserInfoToServer(SendingEvent sendingEvent, 
                                 List<UserDemographics> userDemographics,
                                 List<UserGroup> userGroups, 
                                 List<UserSetupEvent> userSetupEvents)
     {}
}

Таким образом, вопрос заключается в следующем: для юнит-теста SendUserInfoToServer я должен дублировать SetupUserEvent и AddUserDemographicsToGroup в моем тестовом проекте, или я должен просто вызвать их, чтобы помочь мне настроить некоторые "реальные" параметры?

Ответы [ 5 ]

1 голос
/ 03 сентября 2010

Вам нужно проверить дубликаты .

Вы правы, что модульные тесты не должны вызывать другие методы, поэтому вам необходимо "подделать" зависимости.Это можно сделать одним из двух способов:

  1. Ручные тестовые дубликаты
  2. Насмешливые

Тестовые дубликаты позволяют изолировать ваш метод втест из его зависимостей.

Я использую Moq для насмешек.Ваш модульный тест должен отправлять «фиктивные» значения параметров или статически определенные значения, которые вы можете использовать для проверки потока управления:

public class MyTestObject
{
       public List<Thingie> GetTestThingies()
       {
             yield return new Thingie() {id = 1};
             yield return new Thingie() {id = 2};
             yield return new Thingie() {id = 3};
       } 
}

Если метод вызывает любые другие классы / методы, используйте mocks (иначе)фальшивки ").Mocks - это динамически генерируемые объекты, основанные на виртуальных методах или интерфейсах:

Mock<IRepository> repMock = new Mock<IRepository>();
MyPage obj = new MyPage() //let's pretend this is ASP.NET
obj.IRepository = repMock.Object;
repMock.Setup(r => r.FindById(1)).Returns(MyTestObject.GetThingies().First());
var thingie = MyPage.GetThingie(1);

Приведенный выше объект Mock использует метод Setup для возврата того же результата для вызова, определенного в лямбде r => r.FindById(1).Это называется ожиданием .Это позволяет вам тестировать только код в вашем методе, фактически не вызывая какие-либо зависимые классы.

После того, как вы настроили тест таким образом, вы можете использовать функции Moq, чтобы подтвердить, что все произошло так, как он должен был:

//did we get the instance we expected?
Assert.AreEqual(thingie.Id, MyTestObject.GetThingies().First().Id); 
//was a method called?
repMock.Verify(r => r.FindById(1));

Метод Verify позволяет вам тестироватьбыл ли вызван метод.Вместе эти средства позволяют вам сосредоточить свои юнит-тесты на одном методе за один раз.

0 голосов
/ 23 ноября 2010

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

0 голосов
/ 08 сентября 2010

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

0 голосов
/ 03 сентября 2010

Внедрение интерфейсов - это то, что я бы предпочел. Затем вы можете смоделировать используемые классы и вам не нужно дублировать код (что нарушает принцип «Не повторяйте себя»), и вам не нужно использовать оригинальные реализации в модульных тестах для класса Communications.

0 голосов
/ 03 сентября 2010

Звучит так, как будто ваши юниты слишком тесно связаны (по крайней мере, из быстрого взгляда на вашу проблему).Что делает меня любопытным, например, тот факт, что ваши UserGroups принимают UserDemographics, а ваш UserSetupEvent берет список UserGroup, включая список UserDemographics ( снова ).Разве List<UserGroup> не должен уже включать ÙserDemographics, переданный в его конструктор, или я неправильно понимаю?

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

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