Правильный подход к модульному тестированию «сложного» сервисного приложения - PullRequest
1 голос
/ 22 июля 2010

У меня есть служба приложений (т. Е. Большой метод), отвечающая за координацию взаимодействия между несколькими бизнес-объектами.По сути, он берет DTO из одной системы, которая содержит информацию о клиенте и счет-фактуру, и переводит ее и импортирует в другую систему на основе различных бизнес-правил.

public void ProcessQueuedData()
    {
       var queuedItems = _importServiceDAL.LoadQueuedData();

       foreach (var queuedItem in queuedItems)
       {
           var itemType = GetQueuedImportItemType(queuedItem);

           if (itemType == QueuedImportItemType.Invoice)
           {
               var account = _accountDAL.GetAccountByAssocNo(queuedItem.Assoc);
               int agentAccountID;

               if (!account.IsValid)
               {
                   agentAccountId = _accountDAL.AddAccount(queuedItem.Assoc);
               }
               else
               {
                   agentAccountId = account.AgentAccountID;
               }

               /// Do additional processing TBD
           }
       }
    }

Для модульных тестов правильно лиПредположим, что весь процесс в службе должен тестироваться поэтапно, поэтапно, подобно следующему:

ImportService_ProcessQueuedData_CallsDataAccessLayer_ToLoadQueue

ImportService_ProcessQueuedExcessIeedIccessIedI_Ice_Date_Ex_11012 * ImportService_ProcessQueuedData_WithInvoice_CallsDALToCreateAccountIfOneDoesNotExist

Вот типичный тест:

    [TestMethod()]
    public void ImportService_ProcessQueuedData_WithInvoice_CallsDALToCheckIfAgentAccountExists()
    {
        var accountDAL = MockRepository.GenerateStub<IAccountDAL>();
        var importServiceDAL = MockRepository.GenerateStub<IImportServiceDAL>();

        importServiceDAL.Stub(x => x.LoadQueuedData())
            .Return(GetQueuedImportItemsWithInvoice());

        accountDAL.Stub(x => x.GetAccountByAssocNo("FFFFF"))
            .IgnoreArguments()
            .Return(new Account() { AgentAccountId = 0 });

        var importSvc = new ImportService(accountDAL, importServiceDAL);

        importSvc.ProcessQueuedData();

        accountDAL.AssertWasCalled(a => a.GetAccountByAssocNo("FFFFF"), o => o.IgnoreArguments());
        accountDAL.VerifyAllExpectations();
    }

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

Ответы [ 3 ]

0 голосов
/ 23 июля 2010

Лично я бы попробовал протестировать все фрагменты кода, но не обязательно каждый раздел как отдельный тест.Один тест проверяет, что Счет с действующим аккаунтом проходит.Второй тест проверяет, что Счет с неверной учетной записью создает новую учетную запись.Конечно, я бы издевался над далами, чтобы данные не добавлялись в базу данных.Это также позволяет имитировать исключения и случаи, когда никакие операции не должны выполняться (ничего в очереди или, возможно, нет счетов).

0 голосов
/ 30 июля 2010

Я согласен с Педро.Ваш первый пример теста (ImportService_ProcessQueuedData_CallsDataAccessLayer_ToLoadQueue) необязателен, потому что другие тесты будут неявно проверять, что был вызван LoadQueuedData - у них не было бы данных для работы.Вы хотите иметь тест для каждого пути, но вам не нужен отдельный тест для каждой строки кода в методе.Если через условное ветвление есть шесть путей, вам нужно шесть тестов.

Если бы я хотел по-настоящему вычурно, я мог бы также рассмотреть возможность использования полиморфизма объектов, чтобы уменьшить количество операторов if и упростить тесты.Например, вы можете иметь разные «обработчики» для каждого QueuedImportItemType и поместить туда логику для того, что делать с элементом этого типа, вместо вашего большого метода Process.Разделение логики итерации и того, что делать с каждым типом элементов, облегчает их тестирование отдельно.

0 голосов
/ 22 июля 2010

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

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