Skip to content

2. Інтеграція Spring / NHibernate

Фреймворк Spring пропонує допоміжні класи для роботи з фреймворком NHibernate. Використання цих класів спрощує написання коду для доступу до даних SGBD. Розглянемо таку багаторівневу архітектуру:

Далі ми побудуємо шар [dao] на базі [Spring / NHibernate], коментуючи код робочого рішення. Ми не будемо намагатися висвітлити всі можливості конфігурації чи використання фреймворку [Spring / Nhibernate]. Читач зможе адаптувати запропоноване рішення до своїх власних завдань, скориставшись документацією до Spring.NET та [Spring.NET | Homepage ] (грудень 2011 року).

2.1. Рівень доступу до даних [dao]

Базою даних є база MySQL [dbpam_nhibernate], вже представлена в розділі 1.2. Рівень [dao] реалізує наступний інтерфейс C#:


using Pam.Dao.Entites;

namespace Pam.Dao.Service {
    public interface IPamDao {
        // перелік усіх ідентифікаційних даних працівників 
        Employe[] GetAllIdentitesEmployes();
        // конкретний працівник із його виплатами 
        Employe GetEmploye(string ss);
        // перелік усіх внесків 
        Cotisations GetCotisations();
    }
}

2.1.1. Проєкт Visual Studio C# для шару [dao]

Проєкт Visual Studio для шару [dao] має такий вигляд:

  • у [1] — проект у цілому
    • папка [pam] містить класи проекту, а також конфігурацію сутностей NHibernate
    • файли [App.config] та [Dao.xml] налаштовують фреймворк Spring / NHibernate. Нам доведеться описати вміст цих двох файлів.
  • У файлі [2] містяться різні класи проекту
    • у папці [entites] ми знаходимо сутності NHibernate, розглянуті в попередньому проєкті (див. стор. 14)
    • у папці [service] ми знаходимо інтерфейс [IPamDao] та його реалізацію за допомогою фреймворку Spring / NHibernate [PamDaoSpringNHibernate].
    • Папка [tests] містить тести для інтерфейсу [IPamDao].
  • У [3] містяться посилання на проект. Інтеграція Spring / NHibernate вимагає нових DLL та [4].

У посиланнях [3] проекту містяться такі DLL:

  • NHibernate: для ORM та NHibernate
  • MySql.Data: драйвер ADO.NET для SGBD MySQL 5
  • Spring.Core: для фреймворку Spring, який забезпечує інтеграцію рівнів
  • log4net: бібліотека журналів
  • nunit.framework: бібліотека модульних тестів
  • Spring.Aop, Spring.Data та Spring.Data.NHibernate32: забезпечують підтримку Spring / NHibernate.

Ми подбаємо про те, щоб усі ці DLL мали властивість «Локальна копія» у True.

2.1.2. Конфігурація проекту C#

Проєкт налаштовано таким чином:

  • у [1], ім’я збірки проєкту — [pam-dao-spring-nhibernate]. Це ім’я використовується в різних конфігураційних файлах проєкту.

2.1.3. Елементи шару [dao]

Елементи (об’єкти), необхідні для шару [dao], зібрано у папці [entites] [1] цього проєкту. Ці об’єкти є тими самими, що й у попередньому проєкті (див. розділ 1.3.2), за єдиною відмінністю, яка міститься у файлах конфігурації NHibernate. Візьмемо, наприклад, файл [Employe.hbm.xml]:

  • у файлі [2] цей файл налаштовано для включення до збірки проекту

Його вміст такий:


<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Pam.Dao.Entites" assembly="pam-dao-spring-nhibernate">
    <class name="Employe" table="EMPLOYES">
        <id name="Id" column="ID">
            <generator class="native" />
        </id>
        <version name="Version" column="VERSION"/>
        <property name="SS" column="SS" length="15" not-null="true" unique="true"/>
        <property name="Nom" column="NOM" length="30" not-null="true"/>
        <property name="Prenom" column="PRENOM" length="20" not-null="true"/>
        <property name="Adresse" column="ADRESSE" length="50" not-null="true" />
        <property name="Ville" column="VILLE" length="30" not-null="true"/>
        <property name="CodePostal" column="CP" length="5" not-null="true"/>
        <many-to-one name="Indemnites" column="INDEMNITE_ID" cascade="all" lazy="false"/>
    </class>
</hibernate-mapping>
  • рядок 3: атрибут assembly вказує, що файл [Employe.hbm.xml] буде знайдено в збірці [pam-dao-spring-nhibernate]

Крім того, у папці [entites] ми знаходимо клас винятків, який використовується проектом:


using System;
namespace Pam.Dao.Entites {

    public class PamException : Exception {

        // код помилки 
        public int Code { get; set; }

        // виробники 
        public PamException() {
        }

        public PamException(int Code)
            : base() {
            this.Code = Code;
        }

        public PamException(string message, int Code)
            : base(message) {
            this.Code = Code;
        }

        public PamException(string message, Exception ex, int Code)
            : base(message, ex) {
            this.Code = Code;
        }
    }
}

Клас [PamException] було похідним від класу [Exception] (рядок 4) з метою додавання до нього коду помилки (рядок 7).

2.1.4. Конфігурація Spring / NHibernate

Повернемося до проекту Visual C#:

  • у [1] файли [App.config] та [Dao.xml] налаштовують інтеграцію Spring / NHibernate

2.1.4.1. Файл [App.config]

Файл [App.config] має такий вигляд:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!-- розділи налаштувань -->
    <configSections>
        <sectionGroup name="spring">
            <section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core" />
            <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
            <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
        </sectionGroup>
        <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
    </configSections>


    <!-- конфігурація Spring -->
    <spring>
        <parsers>
            <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
        </parsers>
        <context>
            <resource uri="Dao.xml" />
        </context>
    </spring>

    <!-- Цей розділ містить параметри конфігурації log4net -->
    <!-- NOTE IMPORTANTE: журнали за замовчуванням неактивні. Їх потрібно активувати програмно
    avec l'instruction log4net.Config.XmlConfigurator.Configure();
    ! -->
    <log4net>
        <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
            <layout type="log4net.Layout.PatternLayout">
                <conversionPattern value="%-5level %logger - %message%newline" />
            </layout>
        </appender>

        <!-- Встановити рівень журналювання за замовчуванням на DEBUG -->
        <root>
            <level value="DEBUG" />
            <appender-ref ref="ConsoleAppender" />
        </root>

        <!-- Налаштувати журналювання для Spring.  Імена логерів у Spring відповідають простору імен -->
        <logger name="Spring">
            <level value="INFO" />
        </logger>

        <logger name="Spring.Data">
            <level value="DEBUG" />
        </logger>

        <logger name="NHibernate">
            <level value="DEBUG" />
        </logger>
    </log4net>

</configuration>

Вищезазначений файл [App.config] налаштовує Spring (рядки 5–9, 15–22), log4net (рядок 10, рядки 28–53), але не NHibernate. Об’єкти Spring налаштовуються не у файлі [App.config], а у файлі [Dao.xml] (рядок 20). Отже, конфігурація Spring / NHibernate, яка полягає у оголошенні конкретних об’єктів Spring, міститься саме в цьому файлі.

2.1.4.2. Файл [Dao.xml]

Файл [Dao.xml], що містить об’єкти, які керуються Spring, має такий вигляд:


<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net"
         xmlns:db="http://www.springframework.net/database">

    <!-- Посилання міститься у файлі конфігурації основного контексту додатка -->
    <description>
        Application Spring / NHibernate
    </description>

    <!-- База даних та NHibernate Конфігурація -->
    <db:provider id="DbProvider"
                   provider="MySql.Data.MySqlClient"
                   connectionString="Server=localhost;Database=dbpam_nhibernate;Uid=root;Pwd=;"/>

    <object id="NHibernateSessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate32">
        <property name="DbProvider" ref="DbProvider"/>
        <property name="MappingAssemblies">
            <list>
                <value>pam-dao-spring-nhibernate</value>
            </list>
        </property>
        <property name="HibernateProperties">
            <dictionary>
                <entry key="dialect" value="NHibernate.Dialect.MySQL5Dialect"/>
                <entry key="hibernate.show_sql" value="false"/>
            </dictionary>
        </property>
        <property name="ExposeTransactionAwareSessionFactory" value="true" />
    </object>

    <!-- менеджер транзакцій -->
    <object id="transactionManager"
        type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate32">
        <property name="DbProvider" ref="DbProvider"/>
        <property name="SessionFactory" ref="NHibernateSessionFactory"/>
    </object>

    <!-- Шаблон Hibernate -->
    <object id="HibernateTemplate" type="Spring.Data.NHibernate.Generic.HibernateTemplate">
        <property name="SessionFactory" ref="NHibernateSessionFactory" />
        <property name="TemplateFlushMode" value="Auto" />
        <property name="CacheQueries" value="true" />
    </object>

    <!-- Об’єкти доступу до даних -->
    <object id="pamdao" type="Pam.Dao.Service.PamDaoSpringNHibernate, pam-dao-spring-nhibernate" init-method="init" destroy-method="destroy">
        <property name="HibernateTemplate" ref="HibernateTemplate"/>
    </object>
</objects>
  • у рядках 11–13 налаштовується з’єднання з базою даних [dbpam_nhibernate]. У ньому міститься:
    • провайдер ADO.NET, необхідний для підключення; у даному випадку це провайдер SGBD MySQL. Це означає, що у посиланнях проекту мають бути присутні DLL та [Mysql.Data].
    • рядок підключення до бази даних (сервер, назва бази, власник підключення, його пароль)
  • рядки 15–29 налаштовують SessionFactory з NHibernate — об’єкт, який використовується для отримання сесій NHibernate. Нагадаємо, що будь-яка операція з базою даних виконується в межах сесії NHibernate. У рядку 15 видно, що SessionFactory реалізовано класом Spring Spring.Data.NHibernate.LocalSessionFactoryObject, який міститься в DLL Spring.Data.NHibernate32.
  • рядок 16: властивість DbProvider встановлює параметри підключення до бази даних (провайдер ADO.NET та рядок підключення). Тут ця властивість посилається на об’єкт DbProvider, визначений раніше у рядках 11–13.
  • рядки 17–20: визначають список збірників, що містять файли [*.hbm.xml], які налаштовують об’єкти, що керуються NHibernate. Рядок 19 вказує, що ці файли будуть знайдено у збірнику проєкту. Нагадуємо, що ця назва міститься у властивостях проекту C#. Також нагадуємо, що всі файли [*.hbm.xml] було налаштовано для включення до збірки проекту.
  • рядки 22–27: специфічні властивості NHibernate.
    • рядок 24: діалект SQL, що використовується, буде таким самим, як у MySQL
    • рядок 25: SQL, згенерований NHibernate, не з’являтиметься у журналах консолі. Встановлення цього параметра на true дозволяє дізнатися про команди SQL, відправлені NHibernate. Це може допомогти, наприклад, зрозуміти, чому додаток працює повільно під час доступу до бази даних.
  • рядок 28: властивість ExposeTransactionAwareSessionFactory для true призведе до того, що Spring буде обробляти анотації управління транзакціями, які будуть знайдені в коді C#. Ми повернемося до цього, коли будемо писати клас, що реалізує шар [dao].
  • Рядки 32–36 визначають менеджер транзакцій. І тут цей менеджер є класом Spring із DLL Spring.Data.NHibernate32. Цьому менеджеру потрібно знати параметри підключення до бази даних (рядок 34), а також SessionFactory та NHibernate (рядок 35).
  • Рядки 39–43 визначають властивості класу HibernateTemplate, який також є класом Spring. Цей клас буде використовуватися як допоміжний клас у класі, що реалізує рівень [dao]. Він полегшує взаємодію з об’єктами NHibernate. Цей клас має певні властивості, які потрібно ініціалізувати:
    • рядок 40: SessionFactory з NHibernate
    • рядок 41: властивість TemplateFlushMode визначає режим синхронізації контексту збереження NHibernate з базою даних. Режим Auto забезпечує синхронізацію:
      • наприкінці транзакції
      • перед операцією select
    • рядок 42: запити HQL (Hibernate Query Language) будуть кешовані. Це може призвести до підвищення продуктивності.
  • рядки 46–48 визначають клас реалізації шару [dao]
    • рядок 46: шар [dao] буде реалізовано класом [PamdaoSpringNHibernate] з DLL [pam-dao-spring-nhibernate]. Після створення екземпляра класу негайно буде виконано метод init цього класу. Під час закриття контейнера Spring буде виконано метод destroy цього класу.
    • рядок 47: клас [PamDaoSpringNHibernate] матиме властивість HibernateTemplate, яка буде ініціалізована за допомогою властивості HibernateTemplate з рядка 39.

2.1.5. Реалізація шару [dao]

2.1.5.1. Скелет класу реалізації

Інтерфейс [IPamDao] має такий вигляд:


using Pam.Dao.Entites;

namespace Pam.Dao.Service {
    public interface IPamDao {
        // Список усіх ідентифікаторів співробітників 
        Employe[] GetAllIdentitesEmployes();
        // конкретний працівник із його виплатами 
        Employe GetEmploye(string ss);
        // перелік усіх внесків 
        Cotisations GetCotisations();
    }
}
  • рядок 1: імпортується простір імен сутностей шару [dao].
  • рядок 3: шар [dao] знаходиться в просторі імен [Pam.Dao.Service]. Елементи простору імен [Pam.Dao.Entites] можуть створюватися у декількох екземплярах. Елементи простору імен [Pam.Dao.Service] створюються в єдиному екземплярі (singleton). Саме це зумовило вибір назв просторів імен.
  • рядок 4: інтерфейс має назву [IPamDao]. Він визначає три методи:
    • у рядку 6 метод [GetAllIdentitesEmployes] повертає масив об’єктів типу [Employe], який представляє список нянь у спрощеному вигляді (прізвище, ім’я, SS).
    • у рядку 8 метод [GetEmploye] повертає об’єкт [Employe]: працівника, номер соціального страхування якого передано як параметр методу, разом із виплатами, пов’язаними з його індексом.
    • у рядку 10 [GetCotisations] повертає об’єкт [Cotisations], який інкапсулює ставки різних соціальних внесків, що стягуються з валової заробітної плати.

Скелет класу реалізації цього інтерфейсу з підтримкою Spring / NHibernate може виглядати наступним чином:


using System;
using System.Collections;
using System.Collections.Generic;
using Pam.Dao.Entites;
using Spring.Data.NHibernate.Generic.Support;
using Spring.Transaction.Interceptor;

namespace Pam.Dao.Service {
    public class PamDaoSpringNHibernate : HibernateDaoSupport, IPamDao {
        // приватні поля 
        private Cotisations cotisations;
        private Employe[] employes;

        // ініціалізація 
        [Transaction(ReadOnly = true)]
        public void init() {
...
        }

        // видалення об’єкта
        public void destroy() {
            if (HibernateTemplate.SessionFactory != null) {
                HibernateTemplate.SessionFactory.Close();
            }
        }

        // перелік усіх ідентифікаційних даних працівників
        public Employe[] GetAllIdentitesEmployes() {
            return employes;
        }


        // конкретний працівник із його виплатами 
        [Transaction(ReadOnly = true)]
        public Employe GetEmploye(string ss) {
....
        }

        // перелік внесків 
        public Cotisations GetCotisations() {
            return cotisations;
        }
    }
}
  • рядок 9: клас [PamDaoSpringNHibernate] правильно реалізує інтерфейс рівня [dao] [IPamDao]. Він також походить від класу Spring [HibernateDaoSupport]. Цей клас має властивість [HibernateTemplate], яка ініціалізується за допомогою виконаної конфігурації Spring (рядок 2 нижче):

    <object id="pamdao" type="Pam.Dao.Service.PamDaoSpringNHibernate, pam-dao-spring-nhibernate" init-method="init" destroy-method="destroy">
        <property name="HibernateTemplate" ref="HibernateTemplate"/>
</object>
  • У рядку 1 вище видно, що визначення об’єкта [pamdao] вказує на те, що методи init та destroy класу [PamDaoSpringNHibernate] мають виконуватися у певні моменти часу. Ці два методи дійсно присутні в класі у рядках 16 та 21.
  • рядки 15, 34: анотації, які забезпечують виконання анотованого методу в рамках транзакції. Атрибут ReadOnly=true вказує, що транзакція є лише для читання. Метод, що виконується в транзакції, може викликати виняток. У цьому випадку Spring автоматично Rollback транзакцію. Ця анотація усуває необхідність керувати транзакцією всередині методу.
  • рядок 16: метод init виконується Spring одразу після інстанціювання класу. Ми побачимо, що його метою є ініціалізація приватних полів у рядках 11 і 12. Він виконуватиметься в рамках транзакції (рядок 15).
  • Методи інтерфейсу [IPamDao] реалізовано в рядках 28, 35 та 40.
  • рядки 28–30: метод [GetAllIdentitesEmployes] лише повертає атрибут з рядка 12, ініціалізований методом init.
  • рядки 40–42: метод [GetCotisations] лише повертає атрибут з рядка 11, ініціалізований методом init.

2.1.5.2. Корисні методи класу HibernateTemplate

Ми будемо використовувати такі методи класу HibernateTemplate:

IList<T> Find<T>(string requete_hql)
виконує запит HQL і повертає список об’єктів типу T
IList<T> Find<T>(string requete_hql, object[])
виконує запит HQL, параметри якого задаються символами «?». Значення цих параметрів надаються масивом об’єктів.
IList<T> LoadAll<T>()
повертає всі сутності типу T

Існують й інші корисні методи, якими ми не матимемо нагоди скористатися, але які дозволяють знаходити, зберігати, оновлювати та видаляти об’єкти:

T Load<T>(object id)
додає до сесії NHibernate суть типу T з первинним ключем id.
void SaveOrUpdate(object entité)
вставляє (INSERT) або оновлює (UPDATE) об’єкт entité залежно від того, чи має він первинний ключ (UPDATE), чи ні (INSERT). Відсутність первинного ключа можна налаштувати за допомогою атрибута unsaved-values у файлі конфігурації об’єкта. Після операції SaveOrUpdate об’єкт entité перебуває в сесії NHibernate.
void Delete(object entité)
видаляє об’єкт entité із сесії NHibernate.

2.1.5.3. Реалізація методу init

Метод init класу [PamDaoSpringNHibernate] за налаштуванням є методом, що виконується після створення Spring екземпляра класу. Його метою є збереження в локальному кеші спрощених ідентифікаторів співробітників (прізвище, ім’я, SS) та ставок внесків. Його код може виглядати наступним чином.


[Transaction(ReadOnly = true)]
        public void init() {
            try {
                // отримання спрощеного списку співробітників
                IList<object[]> lignes = HibernateTemplate.Find<object[]>("select e.SS,e.Nom,e.Prenom from Employe e");
                // вносимо його в таблицю
                employes = new Employe[lignes.Count];
                int i = 0;
                foreach (object[] ligne in lignes) {
                    employes[i] = new Employe() { SS = ligne[0].ToString(), Nom = ligne[1].ToString(), Prenom = ligne[2].ToString() };
                    i++;
                }
                // вносимо ставки внесків до об’єкта 
                cotisations = (HibernateTemplate.LoadAll<Cotisations>())[0];
            } catch (Exception ex) {
                // перетворення винятку 
                throw new PamException(string.Format("Erreur d'accès à la BD : [{0}]", ex.ToString()), 43);
            }
        }
  • рядок 5: виконується запит HQL. Він запитує поля SS, Прізвище, Ім’я для всіх сутностей Employé. Він повертає список об’єктів. Якби ми запитали повні дані про співробітника у формі «select e from Employe e», то отримали б список об’єктів типу Employe.
  • рядки 7–12: цей список об’єктів копіюється в масив об’єктів типу Employe.
  • рядок 14: запитується список усіх сутностей типу Cotisations. Відомо, що цей список містить лише один елемент. Тому отримується перший елемент списку, щоб отримати ставки внесків.
  • Рядки 7 і 14 ініціалізують два приватні поля класу.

2.1.5.4. Реалізація методу GetEmploye

Метод GetEmploye повинен повернути об’єкт «Співробітник» із заданим номером SS. Його код може виглядати так:


[Transaction(ReadOnly = true)]
        public Employe GetEmploye(string ss) {
            IList<Employe> employés = null;
            try {
                // запит
                employés = HibernateTemplate.Find<Employe>("select e from Employe e where e.SS=?", new object[]{ss});
            } catch (Exception ex) {
                // перетворюємо виняток 
                throw new PamException(string.Format("Erreur d'accès à la BD lors de la demande de l'employé de n° ss [{0}] : [{1}]", ss, ex.ToString()), 41);
            }
            // чи було знайдено працівника? 
            if (employés.Count == 0) {
                // повідомляється про це 
                throw new PamException(string.Format("L'employé de n° ss [{0}] n'existe pas", ss), 42);
            } else {
                return employés[0];
            }
        }
  • рядок 6: отримує список співробітників із заданим номером SS
  • рядок 12: зазвичай, якщо співробітник існує, має бути отримано список з одним елементом
  • рядок 14: якщо це не так, генерується виняток
  • рядок 16: якщо це так, то повертається перший працівник зі списку

2.2. Тестування рівня [dao]

2.2.1. Проєкт Visual Studio

Проект Visual Studio вже було представлено. Нагадаємо:

  • у [1] — проект у цілому
  • у [2] — різні класи проекту. Папка [tests] містить консольний тест [Main.cs] та модульний тест [NUnit.cs].
  • у файлі [3] компілюється програма [Main.cs].
  • у [4] файл [NUnit.cs] не генерується.
  • Проект є консольним додатком. Виконується клас, вказаний у [5], — це клас файлу [Main.cs].

2.2.2. Тестова консольна програма [Main.cs]

Тестова програма [Main.cs] виконується в такій архітектурі:

Вона призначена для тестування методів інтерфейсу [IPamDao]. Ось приклад простого коду:


using System;
using Pam.Dao.Entites;
using Pam.Dao.Service;
using Spring.Context.Support;

namespace Pam.Dao.Tests {
    public class MainPamDaoTests {
        public static void Main() {
            try {
                // інстанціювання шару [dao]
                IPamDao pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
                // список ідентифікаційних даних співробітників 
                foreach (Employe Employe in pamDao.GetAllIdentitesEmployes()) {
                    Console.WriteLine(Employe.ToString());
                }
                // працівник із його виплатами 
                Console.WriteLine("------------------------------------");
                Console.WriteLine(pamDao.GetEmploye("254104940426058"));
                Console.WriteLine("------------------------------------");
                // перелік внесків 
                Cotisations cotisations = pamDao.GetCotisations();
                Console.WriteLine(cotisations.ToString());
            } catch (Exception ex) {
                // відображення винятку 
                Console.WriteLine(ex.ToString());
            }
            //пауза 
            Console.ReadLine();
        }
    }
}
  • рядок 11: до Spring надсилається запит на отримання посилання на рівень [dao].
  • рядки 13–15: тестування методу [GetAllIdentitesEmployes] інтерфейсу [IPamDao]
  • рядок 18: тестування методу [GetEmploye] інтерфейсу [IPamDao]
  • рядок 21: тест методу [GetCotisations] інтерфейсу [IPamDao]

Spring, NHibernate та log4net налаштовуються за допомогою файлу [App.config] , розглянутого в розділі 2.1.4.1.

Виконання з використанням бази даних, описаної в розділі 1.2, дає такий результат у консолі:

1
2
3
4
5
6
[254104940426058,Jouveinal,Marie,,,,]
[260124402111742,Laverti,Justine,,,,]
------------------------------------
[254104940426058,Jouveinal,Marie,5 rue des oiseaux,St Corentin,49203,[2, 2,1, 2,1, 3,1, 15]]
------------------------------------
[3,49,6,15,9,39,7,88]
  • рядки 1–2: 2 працівники типу [Employe] з єдиною інформацією [SS, Nom, Prenom]
  • рядок 4: працівник типу [Employe] із номером соціального страхування [254104940426058]
  • рядок 5: ставки внесків

2.2.3. Одиничні тести з NUnit

Тепер перейдемо до модульного тесту NUnit. Проєкт Visual Studio для шару [dao] зміниться наступним чином:

  • до [1], тестова програма [NUnit.cs]
  • у [2,3], проект згенерує файл DLL з назвою [pam-dao-spring-nhibernate.dll]
  • у [4], посилання на DLL фреймворку NUnit: [nunit.framework.dll]
  • в [5], клас [Main.cs] не буде включено до DLL [pam-dao-spring-nhibernate]
  • у [6] клас [NUnit.cs] буде включено до DLL [pam-dao-spring-nhibernate]

Тестовий клас NUnit має такий вигляд:


using System.Collections;
using NUnit.Framework;
using Pam.Dao.Service;
using Pam.Dao.Entites;
using Spring.Objects.Factory.Xml;
using Spring.Core.IO;
using Spring.Context.Support;

namespace Pam.Dao.Tests {

    [TestFixture]
    public class NunitPamDao : AssertionHelper {
        // шар [dao], що підлягає тестуванню 
        private IPamDao pamDao = null;

        // конструктор 
        public NunitPamDao() {
            // інстанціювання шару [dao]
            pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
        }

        // ініціалізація 
        [SetUp]
        public void Init() {

        }

        [Test]
        public void GetAllIdentitesEmployes() {
            // перевірка кількості співробітників 
            Expect(2, EqualTo(pamDao.GetAllIdentitesEmployes().Length));
        }

        [Test]
        public void GetCotisations() {
            // перевірка ставки внесків 
            Cotisations cotisations = pamDao.GetCotisations();
            Expect(3.49, EqualTo(cotisations.CsgRds).Within(1E-06));
            Expect(6.15, EqualTo(cotisations.Csgd).Within(1E-06));
            Expect(9.39, EqualTo(cotisations.Secu).Within(1E-06));
            Expect(7.88, EqualTo(cotisations.Retraite).Within(1E-06));
        }

        [Test]
        public void GetEmployeIdemnites() {
            // перевірка осіб 
            Employe employe1 = pamDao.GetEmploye("254104940426058");
            Employe employe2 = pamDao.GetEmploye("260124402111742");
            Expect("Jouveinal", EqualTo(employe1.Nom));
            Expect(2.1, EqualTo(employe1.Indemnites.BaseHeure).Within(1E-06));
            Expect("Laverti", EqualTo(employe2.Nom));
            Expect(1.93, EqualTo(employe2.Indemnites.BaseHeure).Within(1E-06));
        }

        [Test]
        public void GetEmployeIdemnites2() {
            // перевірка неіснуючої особи 
            bool erreur = false;
            try {
                Employe employe1 = pamDao.GetEmploye("xx");
            } catch {
                erreur = true;
            }
            Expect(erreur, True);
        }
    }
}
  • рядок 11: клас має атрибут [TestFixture], що робить його тестовим класом [NUnit].
  • рядок 12: клас походить від утилітарного класу AssertionHelper фреймворку NUnit (починаючи з версії 2.4.6).
  • рядок 14: приватне поле [pamDao] є екземпляром інтерфейсу доступу до шару [dao]. Слід зауважити, що тип цього поля — це інтерфейс, а не клас. Це означає, що екземпляр [pamDao] надає доступ лише до методів інтерфейсу [IPamDao].
  • У класі тестуються методи, що мають атрибут [Test]. Для всіх цих методів процес тестування відбувається наступним чином:
    • спочатку виконується метод, що має атрибут [SetUp]. Він слугує для підготовки ресурсів (мережевих з’єднань, з’єднань із базами даних тощо), необхідних для тестування.
    • потім виконується метод, що тестується
    • і, нарешті, виконується метод з атрибутом [TearDown]. Він, як правило, слугує для звільнення ресурсів, задіяних методом з атрибутом [SetUp].
  • У нашому тесті немає ресурсів, які потрібно виділяти перед кожним тестом і потім звільняти. Тому нам не потрібні методи з атрибутами [SetUp] та [TearDown]. Для прикладу ми навели у рядках 23–26 метод з атрибутом [SetUp].
  • рядки 17–20: конструктор класу ініціалізує приватне поле [pamDao] за допомогою Spring та [App.config].
  • рядки 29–32: тестують метод [GetAllIdentitesEmployes]
  • рядки 35–42: тестують метод [GetCotisations]
  • рядки 45–53: тестують метод [GetEmploye]
  • рядки 56–65: тестують метод [GetEmploye] під час виникнення винятку.

Під час генерації проєкту у папці [bin/Release] створюються файли DLL та [pam-dao-spring-nhibernate.dll]:

Завантажуємо DLL та [pam-dao-spring-nhibernate.dll] за допомогою інструменту [NUnit-Gui], версія 2.5, і виконуємо тести:

Image

Як показано вище, тести пройшли успішно.

2.2.4. Генераці та DLL з шару [dao]

Після написання та тестування класу [PamDaoNHibernate] ми згенеруємо DLL з шару [dao] наступним чином:

  • [1], тестові програми виключаються зі збірки проекту
  • [2,3], налаштування проекту
  • [4], генерація проекту
  • DLL генерується у папці [bin/Release] [5].