tutorials.de Buch-Aktion 02/2012
ERLEDIGT
NEIN
ANTWORTEN
1
ZUGRIFFE
6373
EMPFEHLEN
  • An Twitter übertragen
  • An Facebook übertragen
AUF DIESES THEMA
ANTWORTEN
  1. #1
    Konstantin Denerz Tutorials.de Gastzugang
    Hallo,


    hier ist ein kleines Beispiel zum Einsatz von Spring.Net mit Ado.Net beim Unit-Tests mit NUnit.

    Hierbei werden 2 Methoden eins DAO's (Data Access Object) getestet.

    hier ist das DAO Interface:
    Code csharp:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    using System;
    using System.Collections.Generic;
    using Training.NUnit.SpringWithAdo.Domain;
    namespace Training.NUnit.SpringWithAdo.Dao
    {
     
        public interface IBubuDao
        {
            IList<Bubu> GetBubuByName(string name);
            void SaveBubu(Bubu bubu);
        }
    }

    das Interface IBubuDao hat 2 Methoden:
    • GetBubuByName, die eine Liste von Bubus laden sollte, falls der Name der Bubus gleich dem Wert des Parameters name ist
    • und SaveBubu, die das Geschäftsobjekt Bubu in der Datenbank dauerhaft (persistent) speichern sollte.

    das Geschäftsobjekt Bubu:

    Code csharp:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    
    using System;
     
    namespace Training.NUnit.SpringWithAdo.Domain
    {
     
        public class Bubu
        {
            private long id;
            private string name;
            
            public long Id {
                get { return id; }
                set { id = value; }
            }
            
            public string Name {
                get { return name; }
                set { name = value; }
            }
            public Bubu(){
                
            }
            public Bubu(long id, string name)
            {
                this.id = id;
                this.name = name;
            }
            
        }
    }

    Die Implementierung des Interfaces IBubuDao:
    Code csharp:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    
    using System;
    using System.Collections.Generic;
    using Training.NUnit.SpringWithAdo.Domain;
    using System.Data;
    using Spring.Data;
    namespace Training.NUnit.SpringWithAdo.Dao.Internal
    {
     
        public class BubuDao:AdoDaoSupport,IBubuDao
        {
            /// <summary>
            /// Lädt alle Bubus mit einem bestimmten Namen
            /// </summary>
            /// <param name="name">Name der Bubus, die geladen werden sollen</param>
            /// <returns>Liste mit Bubus</returns>
            public IList<Bubu> GetBubuByName(string name)
            {
                return (IList<Bubu>) this.AdoTemplate.QueryWithResultSetExtractor(
                            CommandType.Text,"SELECT * FROM Bubu WHERE name='"+name+"'",
                            new BubuResultSetExtractor());
            }
            
            
            /// <summary>
            /// Speichert Bubu in der DB
            /// </summary>
            /// <param name="bubu">Bubu-Objekt</param>
            public void SaveBubu(Bubu bubu)
            {
                this.AdoTemplate.ExecuteNonQuery(CommandType.Text,
                            "INSERT INTO bubu values ("+string.Join(", ",
                             new string[]{"'"+bubu.Id.ToString()+"'","'"+bubu.Name+"'"})
                                                    +")");
            }
        }
    }
    In dieser Implementierung erbt BubuDao von der Spring-Klasse AdoDaoSupport. Diese stellt Klassen und Methoden für vereinfachte Datenbankzugriffe bereit. Eine dieser Klassen ist die Klasse AdoTemplate. In dem Beispiel werden 2 Methoden der Klasse AdoTemplate verwendet:
    • ExecuteNonQuery kapselt die gleichnamige Methode der Schnittstelle IDbCommand im Assembly System.Data
    • QueryWithResultSetExtractor kapselt die Methode ExecuteReader der Schnittstelle IDbCommand im Assembly System.Data und verwendet für das Ergebnis eine Implementierung von IResultSetExtractor, der über Ergebnis (vom Typ IDataReader) iteriert und ein Ergebnisobjekt erstellt.

    Die Implementierung des Interfaces IResultSetExtractor
    Code csharp:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    
    using System;
    using System.Collections.Generic;
    using Spring.Data;
    using Training.NUnit.SpringWithAdo.Domain;
    namespace Training.NUnit.SpringWithAdo.Dao.Internal
    {
     
        public class BubuResultSetExtractor:IResultSetExtractor
        {
        
            public object ExtractData(System.Data.IDataReader reader)
            {
                      
                IList<Bubu> result=new List<Bubu>();
                while(reader.Read()){
                    Bubu bubu=new Bubu();
                    bubu.Id=long.Parse(reader["id"].ToString());
                    bubu.Name=reader["name"].ToString();
                    
                    result.Add(bubu);
                    
                }
                return result;
            }
        }
    }

    Das App.config File (Anwendungskonfiguration):
    Code xml:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
        <configSections>
            <sectionGroup name="spring">
                <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
                <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
                <section name="parsers" type="Spring.Context.Support.ConfigParsersSectionHandler, Spring.Core" />
            </sectionGroup>
            <section name="DatabaseConfiguration" type="System.Configuration.NameValueSectionHandler" />
        </configSections>
        <spring>
            <parsers>
                <parser type="Spring.Remoting.RemotingConfigParser, Spring.Services" />
                <parser namespace="http://www.springframework.net/database" type="Spring.Data.DatabaseConfigParser, Spring.Data" schemaLocation="assembly://Spring.Data/Spring.Data/spring-database.xsd" />
            </parsers>
            <context caseSensitive="false" name="spring.root">
                <resource uri="~/Config/Spring/dao.xml" />
            </context>
        </spring>
        <DatabaseConfiguration>
            <add key="db.connectionstring" value="DRIVER={MaxDB};Datasource Name=meinDatasourcename;Server=localhost;UID=meinName;Pwd=meinPasswort;Database=meineDatenbank" />
        </DatabaseConfiguration>
    </configuration>

    Die Springkonfiguration:
    Code xml:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    <?xml version="1.0" encoding="utf-8"?>
    <objects xmlns="http://www.springframework.net" xmlns:d="http://www.springframework.net/database">
        <d:dbProvider id="DbProvider" provider="System.Data.Odbc" connectionString="${db.connectionstring}" />
        <object id="adoTemplate" type="Spring.Data.AdoTemplate, Spring.Data">
            <property name="DbProvider" ref="DbProvider" />
        </object>
        <object name="appConfigPropertyHolder" type="Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer, Spring.Core">
            <property name="configSections">
                <value>DatabaseConfiguration</value>
            </property>
        </object>
        <object name="BubuDao" type="Training.NUnit.SpringWithAdo.Dao.Internal.BubuDao, Training.NUnit.SpringWithAdo">
            <property name="adoTemplate" ref="adoTemplate">
            </property>
        </object>
    </objects>

    Und zuletzt der Unit-Test:
    Code csharp:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    
    #if TEST 
     
    using NUnit.Framework;
    using System;
    using System.Data;
    using System.Collections.Generic;
    using Training.NUnit.SpringWithAdo.Domain;
    using Training.NUnit.SpringWithAdo.Dao;
    using Spring.Data;
    using Spring.Testing.NUnit;
    namespace Training.NUnit.SpringWithAdo
    {
        [TestFixture]
        public class BubuTest:AbstractDependencyInjectionSpringContextTests
        {
            private IBubuDao bubuDao;
            public IBubuDao BubuDao {
                get { return bubuDao; }
                set { bubuDao = value; }
            }
            
            private AdoTemplate template;
            public AdoTemplate Template {
                get { return template; }
                set { template = value; }
            }
                    
            protected override string[] ConfigLocations {
                get { return new string[]{
                    @"Config\Spring\dao.xml"
                    }; }
            }
            
            [Test]
            public void GetBubus()
            {
                //speichert Bubu für den Test
                this.Template.ExecuteNonQuery(CommandType.Text,"insert into bubu values (5,'FooBar')");
                //prüft, ob ein Bubu mit dem Namen FooBar angelegt wurde
                IList<Bubu> list = this.BubuDao.GetBubuByName("FooBar");
                Assert.IsTrue(list.Count>0,"Liste der Bubus ist 0. Es wurden keine Bubus geladen/angelegt.");
            }
            
            [Test]
            public void SaveBubu(){
                this.BubuDao.SaveBubu(new Bubu(1,"BarFoo"));
                //prüft, ob ein Bubu mit der ID = 1 angelegt wurde
                DataTable dataTable = this.Template.DataTableCreate(CommandType.Text,"select * from bubu where id = 1");
                Assert.IsTrue(dataTable.Rows.Count!=0,"Anzahl der Bubus ist 0. Es wurden keine Bubus gespeichert.");
            }
     
            protected override void OnTearDown()
            {
                base.OnTearDown();
                //räumt auf
                this.Template.ExecuteNonQuery(CommandType.Text,"delete from bubu where id = 1 or id = 5");
            }       
        }
    }
    #endif

    Die Klasse BubuTest erbt von der Spring-Klasse AbstractDependencyInjectionSpringContextTests aus dem Assembly Spring.Testing.NUnit, die die 2 Methoden SetUp und TearDown schon implementiert. Nach dem die Eigenschaft ConfigLocations überschrieben wird und man im Getter die Pfade zu den Springkonfigurationsdateien einträgt, wird beim Ausführen des Tests in der SetUp-Methode ein ApplicationContext erstellt.
    Weiterhin werden die Abhängigkeiten durch Dependency Injection (DI) automatisch aufgelöst (Autowiring). (falls es nicht deaktiviert wird) So bekommen die 2 Attribute, bubuDao und template, Objekte gleichen Typs zugewiesen, die in der Springkonfiguration definiert sind. Beim DI bekommt template über den Typ das entsprechende Objekt zugewiesen, da der Name ungleich dem ist, der in der Springkonfiguration definiert ist.

    In der Springkonfiguration ist der DBProvider System.Data.Odbc definiert. Wer diesen benutzen will muss folgende Springkonfiguration in die Datei dbproviders.xml in dem Projekt Spring.Data im Verzeichnis Data/Common einfügen:
    Code xml:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    
    <object id="Odbc-2.0" type="Spring.Data.Common.DbProvider, Spring.Data" singleton="false">
        <constructor-arg name="dbMetaData">
          <object type="Spring.Data.Common.DbMetadata">
            <constructor-arg name="productName" value="Odbc"/>
            <constructor-arg name="assemblyName" value="System.Data" />
            <constructor-arg name="connectionType" value="System.Data.Odbc.OdbcConnection, System.Data"/>        
            <constructor-arg name="commandType" value="System.Data.Odbc.OdbcCommand, System.Data" />
            <constructor-arg name="parameterType" value="System.Data.Odbc.OdbcParameter, System.Data" />
            <constructor-arg name="dataAdapterType" value="System.Data.Odbc.OdbcDataAdapter, System.Data" />
            <constructor-arg name="commandBuilderType" value="System.Data.Odbc.OdbcCommandBuilder, System.Data" />
            <constructor-arg name="commandBuilderDeriveParametersMethod" value="DeriveParameters"/>
            <constructor-arg name="parameterDbType" value="System.Data.Odbc.OdbcType, System.Data" />
            <constructor-arg name="parameterDbTypeProperty" value="OdbcType"/>
            <constructor-arg name="parameterIsNullableProperty" value="IsNullable"/>
            <constructor-arg name="parameterNamePrefix" value="?"/>
            <constructor-arg name="exceptionType" value="System.Data.Odbc.OdbcException, System.Data"/>
            <constructor-arg name="useParameterNamePrefixInParameterCollection" value="false"/>
            <constructor-arg name="bindByName" value="false"/>
            <constructor-arg name="errorCodeExceptionExpression" value="Errors[0].NativeError.ToString()"/>
            <property name="ErrorCodes.BadSqlGrammarCodes">
              <value>156,170,207,208</value>
            </property>
            <property name="ErrorCodes.PermissionDeniedCodes">
              <value>229</value>
            </property>
            <property name="ErrorCodes.DataIntegrityViolationCodes">
              <value>2627,8114,8115</value>
            </property>
            <property name="ErrorCodes.DeadlockLoserCodes">
              <value>1205</value>
            </property>
          </object>
        </constructor-arg>
     
      </object>
    <alias name="Odbc-2.0" alias="System.Data.Odbc"/>

    Hier findet man die vorhanden DBProvider aufgelistet: DB-Provider




    Gruß Konstantin
    Geändert von Konstantin Denerz (31.07.07 um 08:35 Uhr)
     

  2. #2
    Registriert seit
    Jun 2002
    Ort
    Saarbrücken (Saarland)
    Beiträge
    9.724
    Blog-Einträge
    29
    Hallo,

    erstmal cooles Beispiel
    Hier meine Verbesserungsvorschläge:

    DAO Interface: hier würde ich ein generisches DAO Interface Vorschlagen
    Beispielsweise so:
    Code csharp:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    using System;
    using System.Collections.Generic;
    using System.Text;
     
    namespace De.Tutorials.Training
    {
        public interface IGenericDAO<TEntity,TKey>
        {
            TKey MakePersistent(TEntity entity);
            void MakeTransient(TEntity entity);
            TEntity GetByKey(TKey key);
            List<TEntity> FindByAttribute(string attributeName, object attributeValue);
            List<TEntity> FindByAttributes(string[] attributeNames, object[] attributeValues);
            List<TEntity> FindAll();
        }
    }

    Business Objects sollte man immer mit einer entsprechenden ToString() Methode ausstatten.
    Neben dem technischen/künstlichen Schlüssel/surrogate key (id) auch entsprechende business keys
    (im Sinne der Anwendungsdomäne Eindeutigekennung eines Business Objects -> Sozialversicherungsnummer,
    AccountName, etc.) definieren.

    Equals/GetHashCode so definieren, dass (nur) die Felder des BusinessKey's verwendet werden,
    nicht der technische Schlüssel. Equals/ GetHashCode so implementieren, dass wenn zwei Entitäten
    den gleichen Hashcode haben sie auch Equals sind. Equals Transitiv definieren:
    a.Equals(b); -> b.Equals(c); -> a.Equals(c); -> b.Equals(a) ... etc.

    SQL Statements in konfigurationsdatei auslagern. Parameterisierte SQL Statements benutzen:
    INSERT INTO bubu VALUES (:name,:dateOfBirth, :homeTown);

    ...

    Gruß Tom
     
    Java rocks!
    How to become a good Java Programmer?
    Does IT in Java and .Net
    The only valid measurement of code quality: WTFs / minute
    Blog
    Xing
    Twitter

Ähnliche Themen

  1. Ein paar Fragen zu Unit-Testing
    Von sepan im Forum Coders Talk
    Antworten: 2
    Letzter Beitrag: 18.01.08, 19:08
  2. PHPUnit 3 - Unit Testing
    Von kela_root im Forum PHP
    Antworten: 0
    Letzter Beitrag: 04.12.07, 17:45
  3. Antworten: 0
    Letzter Beitrag: 17.08.06, 22:22
  4. GUI Unit Testing in C#
    Von dreadread im Forum .NET Archiv
    Antworten: 0
    Letzter Beitrag: 25.02.04, 09:15