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



    
rambler's top100 Rambler's Top100