.NETの開発者向けにDevExpress™-eXpress Persistent Objects™(XPO)のORMを使用した場合に表示されるテスト機能についてお話します。
まず、特定のDBMSからの抽象化。
第二に、開発の初期段階およびテスト中に、DBMSはまったく必要ありません。
データベースの構造から始めましょう。
...
using DevExpress.Xpo;
namespace PrimerDlyaHabr {
public class Person : XPObject {
[Indexed( "LastName" , Unique = true )]
public string FirstName;
public string LastName;
public Decimal Wage;
[Association]
public Department Department;
public Person(Session session) : base (session) { }
}
public class Department : XPObject {
[Indexed(Unique = true )]
public string Name;
[Association]
public XPCollection<Person> Staff { get { return GetCollection<Person>( "Staff" ); } }
public Department(Session session) : base (session) { }
}
}
* This source code was highlighted with Source Code Highlighter .
部門(Department)とその中の従業員(Person)を表す2つのクラス(テーブル)があります。
それでは、それらのロジックをいくつか書いてみましょう。
...
using DevExpress.Xpo;
using DevExpress.Xpo.DB.Exceptions;
using DevExpress.Data.Filtering;
using DevExpress.Xpo.Metadata;
namespace PrimerDlyaHabr {
class PersonWork {
IDataLayer dataLayer;
// XPO
public PersonWork(IDataLayer dataLayer) {
this .dataLayer = dataLayer;
UpdateSchema();
}
// . .
void UpdateSchema(){
XPClassInfo[] classInfoList = new XPClassInfo[2];
classInfoList[0] = dataLayer.Dictionary.QueryClassInfo( typeof (Person));
classInfoList[1] = dataLayer.Dictionary.QueryClassInfo( typeof (Department));
dataLayer.UpdateSchema( false , classInfoList);
}
public void AddDepartment( string name) {
using (UnitOfWork session = new UnitOfWork(dataLayer)) {
Department dep = new Department(session);
dep.Name = name;
dep.Save();
session.CommitChanges();
}
}
public bool RemoveDepartment( string name){
using (UnitOfWork session = new UnitOfWork(dataLayer)){
Department dep = GetDepartment(session, name);
if (dep == null ) return false ;
session.Delete(dep.Staff);
session.Delete(dep);
session.CommitChanges();
return true ;
}
}
Department GetDepartment(UnitOfWork session, string name) {
return session.FindObject<Department>(CriteriaOperator.Parse( "Name = ?" , name));
}
public int AddPerson( string firstName, string lastName, Decimal wage, string departmentName) {
using (UnitOfWork session = new UnitOfWork(dataLayer)) {
Department dep = GetDepartment(session, departmentName);
if (dep == null ) throw new ArgumentException( string .Format( "Department '{0}' not found" , departmentName));
Person person = new Person(session);
person.FirstName = firstName;
person.LastName = lastName;
person.Wage = wage;
person.Department = dep;
person.Save();
session.CommitChanges();
return person.Oid;
}
}
public bool RemovePerson( int oid) {
using (UnitOfWork session = new UnitOfWork(dataLayer)) {
Person person = session.GetObjectByKey<Person>(oid);
if (person == null ) return false ;
session.Delete(person);
session.CommitChanges();
return true ;
}
}
CriteriaOperator GetSummaryCriteria( string departmentName) {
return string .IsNullOrEmpty(departmentName) ? null : CriteriaOperator.Parse( "Department.Name = ?" , departmentName);
}
public Decimal CalcWageSummary() {
return CalcDeparmentWageSummary( null );
}
public Decimal CalcDeparmentWageSummary( string name) {
using (UnitOfWork session = new UnitOfWork(dataLayer)) {
return (Decimal)session.Evaluate<Person>(CriteriaOperator.Parse( "Sum(Wage)" ), GetSummaryCriteria(name));
}
}
public int GetPersonCount() {
return GetDeparmentPersonCount( null );
}
public int GetDeparmentPersonCount( string name) {
using (UnitOfWork session = new UnitOfWork(dataLayer)) {
return ( int )session.Evaluate<Person>(CriteriaOperator.Parse( "Count()" ), GetSummaryCriteria(name));
}
}
}
}
* This source code was highlighted with Source Code Highlighter .
これで、オブジェクトからいくつかのアクションを実行するPersonWorkクラスができました(部門/人を追加/削除し、給与と従業員数を考慮します)。
コンストラクターで、クラスはIDataLayerインターフェイス(XPOのデータソースインターフェイス)を受け取ります。
このクラスをテストするには、NUnitフレームワークを使用します。
...
using System.Data;
using DevExpress.Xpo;
using DevExpress.Xpo.DB;
using NUnit.Framework;
namespace PrimerDlyaHabr {
[TestFixture]
public class PersonTests {
PersonWork personWork;
[SetUp]
public void SetUp() {
//
IDataStore dataStore = new InMemoryDataStore( new DataSet(), AutoCreateOption.DatabaseAndSchema);
//
IDataLayer dataLayer = new SimpleDataLayer(dataStore);
//
personWork = new PersonWork(dataLayer);
}
[Test]
public void Wage() {
personWork.AddDepartment( "Main" );
personWork.AddPerson( "Vasya" , "Pupkin" , 100, "Main" );
personWork.AddPerson( "Petya" , "Vasin" , 150, "Main" );
personWork.AddDepartment( "Additional" );
personWork.AddPerson( "Kostya" , "Kostin" , 90, "Additional" );
personWork.AddPerson( "Katya" , "Morozova" , 90, "Additional" );
Assert.AreEqual(100 + 150 + 90 + 90, personWork.CalcWageSummary());
Assert.AreEqual(100 + 150, personWork.CalcDeparmentWageSummary( "Main" ));
Assert.AreEqual(90 + 90, personWork.CalcDeparmentWageSummary( "Additional" ));
Assert.AreEqual(4, personWork.GetPersonCount());
Assert.AreEqual(2, personWork.GetDeparmentPersonCount( "Main" ));
Assert.AreEqual(2, personWork.GetDeparmentPersonCount( "Additional" ));
}
}
}
* This source code was highlighted with Source Code Highlighter .
出力は、1つのテストで1つのクラスのテストを受け取りました。 テストを実行するために、DBMSを使用可能にする必要はありません。
これらの目的のために、メモリ内のデータストアであり、腸でDataSetを使用するInMemoryDataStoreクラスがあります。
したがって、このDataSetを使用して、リポジトリからXMLに情報を保存できます。
このテストでは、2つの部門と4人の従業員を追加し、給与額と従業員数の計算方法を確認します。
テストは成功しました...システムの準備ができました...
次に、実際の条件で使用する必要があります。
これを行うには、フォームを追加し、コンストラクターでpersonWorkのインスタンスを作成します。
...
using DevExpress.Xpo.DB;
using DevExpress.Xpo;
...
PersonWork personWork;
public Form1() {
InitializeComponent();
// MSSql Server
string connectionString = MSSqlConnectionProvider.GetConnectionString( "Server" , "Database" );
// MSSql Server
IDataStore provider = XpoDefault.GetConnectionProvider(connectionString, AutoCreateOption.DatabaseAndSchema);
//
IDataLayer dataLayer = new SimpleDataLayer(provider);
//
personWork = new PersonWork(dataLayer);
}
...
* This source code was highlighted with Source Code Highlighter .
//そして、目的のためにクラスを使用します...
つまり どのDBMSで動作するかを考えずに、既製のビジネスロジッククラスを取得しました...
終わり。