(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Создание приложения на WPF с использованием принципов TDD. Часть 1

Источник: codingclub

В этой части мы создадим скелет нашего приложения. И первый вопрос, который необходимо решить, это определиться с названием приложения. Есть open-source проект WixEdit, схожий по назначению. Но мы на создание и редактирование проекта не замахиваемся, мы будем только отображать существующий проект, и основная функция будет сборка дистрибутива. После недолгих раздумий принимаем решение назвать проект WixMaker. Создаем в Visual Studio новый проект WixMaker из шаблона WPF Application и добавляем в него ссылки на сборки Composite Application Library. По шагам процесс создания проекта с использованием CAL описан в How to: Create a Solution Using the Composite Application Library.

Также подсоединяем проект к системе контроля версий, например SVN

Это будет главный подпроект, который будет заведовать только сборкой модулей и запуском основного окна приложения. Все эти действия будет выполнять класс Bootstrapper. Основная функциональность будет разбита на слабосвязанные модули. Создадим макет внешнего вида приложения, а точнее разобьем содержимое главного окна на регионы. Минимальный набор это меню и основная часть, где выводится содержимое проекта. В первом приближении компоновка окна следующая:

<Window x:Class="WixMaker.Shell"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:cal="clr-namespace:Microsoft.Practices.Composite.Wpf.Regions;assembly=Microsoft.Practices.Composite.Wpf"

    Title="WixMaker" Height="400" Width="600">

    <Grid>

        <Grid>

            <Grid.RowDefinitions>

                <RowDefinition Height="Auto" />

                <RowDefinition Height="*" />

            </Grid.RowDefinitions>

            <ItemsControl Name="MenuRegion" cal:RegionManager.RegionName="MenuRegion" />

            <ContentControl Name="MainRegion" cal:RegionManager.RegionName="MainRegion" Grid.Row="1" />

        </Grid>

    </Grid>

</Window>

Пока на этом остановимся и займемся модулем. Создадим модуль, который будет заведовать показом содержимого WiX проекта и обработкой команд из меню. Делаем новый подпроект WixProject и в нем класс WixProjectModule. Он будет служить основным связующим звеном для связи содержимого модуля с внешним миром. Теперь мы наследуем класс WixProjectModule от интерфейса IModule, и с помощью IntelliSense создаем реализацию единственного метода этого интерфейса Initialize.

#region IModule Members

public void Initialize()

{

    throw new NotImplementedException();

}

#endregion

Также пропишем наш модуль в загрузчике.

public class Bootstrapper : UnityBootstrapper

{

    protected override DependencyObject CreateShell()

    {

        Shell shell = Container.Resolve<Shell>();

        shell.Show();

        return shell;

    }

    protected override IModuleEnumerator GetModuleEnumerator()

    {

        return new StaticModuleEnumerator().

            AddModule(typeof(WixProjectModule));

    }

}

Это последний шаг, который мы может сделать без тестов. Далее создаем тест для WixProjectModule для метода Initialize, воспользовавшись поддержкой Visual Studio Team System. Щелкнем на названии функции Initialize() и из контекстного меню выберем пункт "Create Unit Tests...".

Согласимся с подтверждающим диалогом, а в диалоге создания нового проекта изменим название проекта на WixProject.Test. Здесь мы следуем методике Microsoft, и называем подпроект с тестами по имени подпроекта, который он тестирует, с добавлением ".Test" в название. Далее студия создаст проект, класс теста для WixProjectModule и тестовый метод InitializeTest() и начальным содержимым.

/// <summary>

///A test for Initialize

///</summary>

[TestMethod()]

public void InitializeTest()

{

    WixProjectModule target = new WixProjectModule(); // TODO: Initialize to an appropriate value

    target.Initialize();

    Assert.Inconclusive("A method that does not return a value cannot be verified.");

}

Мне очень нравится поддержка со стороны Visual Studio в данном случае. Во-первых, проделана большая рутинная работа, а во-вторых, в сгенеренном коде нет ни одной лишней строчки.

Теперь надо решить, что мы будем реализовывать и соответственно проверять. Пойдем по пути наименьших усилий и добавим меню. Так как после добавления меню должно появиться в регионе MenuRegion, то давайте будем проверять, что там что-то появилось. Но для этого надо сделать несколько подготовительных операций. Для начала надо откуда-то получить ссылку на RegionManager, куда мы будем добавлять меню. Это делается просто:

private readonly IRegionManager _regionManager;

public WixProjectModule(IRegionManager regionManager)

{

    _regionManager = regionManager;

}

Не удивляйтесь, но этого будет достаточно. Если вы поставите точку останова в конструкторе и запустите проект, то увидите, что regionManager содержит реальный объект. Первый раз, увидев такой код в выдернутом из недр интернета примере, я ничего не понял. Откуда возьмется реализация IRegionManager? Собственно это и явилось побудительной причиной познакомиться с Composite Application Library. Здесь мы в первый раз в нашем проекте сталкиваемся с паттерном Inversion of Control (IoC) который в данном случае реализован через другой, более специализированный паттерн Dependency Injection (DI). А это значит, что всю необходимую работу для нас делает Unit Container. Помимо того что мы избежали внесения сильной связи в наш модуль, а то и еще хуже Singleton, мы получили отличную возможность для модульного тестирования, чем мы сейчас и воспользуемся. Для этого в нашем тесте вместо реального RegionManager мы передадим поддельный объект (Mock object) с необходимой нам для теста функциональностью.

public class MockRegionManager : IRegionManager

{

    private Dictionary<string, IRegion> _regions = new Dictionary<string, IRegion>();

    public IDictionary<string, IRegion> Regions

    {

        get { return _regions; }

    }

    public void AttachNewRegion(object regionTarget, string regionName)

    {

        throw new NotImplementedException();

    }

    public IRegionManager CreateRegionManager()

    {

        throw new NotImplementedException();

    }

}

Как видите, код подделки прост. И он и не должен быть сложным. Мы не пытаемся полностью сымитировать поведение объекта, а только ту минимальную часть, которая нужна нам для конкретного теста. Если для другого теста нам понадобится дополнительная функциональность, надо будет оценить, что лучше: дописать новую функциональность в уже существующий поддельный объект, с вероятностью, что это затронет уже отлаженные тесты. Или написать новую реализацию поддельного объекта, только с той функциональностью, которая нужна для нового теста. Подобным образом реализуем поддельный объект для IRegion. Теперь у нас есть все для первого теста.

Возвращаемся к нашему тесту, а конкретно к строчке с TODO, который предлагает нам дописать код инициализации. Как видим, комментарий был поставлен не зря. Заменяем эту строчку на

var regionManager = new MockRegionManager();

WixProjectModule target = new WixProjectModule(regionManager);

Также добавляем поддельный регион для меню

var menuRegion = new MockRegion();

regionManager.Regions.Add("MenuRegion", menuRegion);

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

Assert.AreEqual(1, menuRegion.AddedViews.Count);

Полностью тест выглядет так

[TestMethod]

public void InitializeTest()

{

   var regionManager = new MockRegionManager();

   WixProjectModule target = new WixProjectModule(regionManager);

   var menuRegion = new MockRegion();

   regionManager.Regions.Add("MenuRegion", menuRegion);

   target.Initialize();

   Assert.AreEqual(1, menuRegion.AddedViews.Count);

}

Тест готов. Нам надо его запустить, чтобы получить первоначальный негативный результат. Это важный момент. Если вы сначала напишите функциональность, а потом тест, и он сразу пройдет, то это не значит, что новый кусок кода работает правильно. Может быть, тест проходит и без него. Запустить тест можно или из контекстного меню, встав внутрь тестового метода и выбрав "Run Tests" или через меню Test->Run->Test In Current Context.Получаем первый результат

Полностью Error Message:

Test method WixProject.Test.WixProjectModuleTest.InitializeTest threw exception:  System.NotImplementedException: The method or operation is not implemented..

Ну что же, мы получили красный цвет и конкретное сообщение, которое указывает что делать дальше. Создаем MainMenu класс, отнаследованный от UserControl.

<UserControl x:Class="WixProject.MainMenu"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Grid>

        <Menu Grid.Row="0" IsMainMenu="True">

            <MenuItem Header="File">

                <MenuItem Header="Open" />

            </MenuItem>

        </Menu>

    </Grid>

</UserControl>

Наконец добрались до сути и добавляем наше меню в регион

public void Initialize()

{

   IRegion menuRegion = _regionManager.Regions["MainMenu"];

   menuRegion.Add(new MainMenu());

}

Здесь мы использовали еще одну специализированную реализацию шаблона IoC, а именно шаблон Service Locator. Запускаем заново тест, и получаем опять красный цвет и сообщение:

Test method WixProject.Test.WixProjectModuleTest.InitializeTest threw exception:  System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary..   

Так, ситуация проясняется. Мы ошиблись в названии региона. Если бы не тест, мы при запуске приложения просто бы увидел пустое окно, и нам бы просто оставалось гадать, в чем дело. Исправляем название региона на MenuRegion, запускаем тест снова и наконец видим зеленый цвет.

Запускаем наше приложение, видим окно приложения с меню.

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

Ссылки по теме


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 09.02.2010 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Microsoft Office 365 Профессиональный Плюс. Подписка на 1 рабочее место на 1 год
Microsoft Office 365 Персональный 32-bit/x64. 1 ПК/MAC + 1 Планшет + 1 Телефон. Все языки. Подписка на 1 год.
Microsoft Windows Professional 10, Электронный ключ
Microsoft 365 Apps for business (corporate)
Microsoft 365 Business Standard (corporate)
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Безопасность компьютерных сетей и защита информации
Новости ITShop.ru - ПО, книги, документация, курсы обучения
Программирование на Microsoft Access
CASE-технологии
Компьютерные книги. Рецензии и отзывы
Мир OLAP и Business Intelligence: новости, статьи, обзоры
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100