ObjectMotherを使用したTableModuleのテスト(^o^)
ObjectMotherを使用すれば、テストデータに意味を持たせて開発者間で共有することができる。
今回は型なしDataSetでのTableModuleのテストにObjectMotherを使用してみた。
まずはOrderDetailというTableModuleのテストコードから。本来のObjectMotherパターンであればOrderDetail自体のインスタンスを返すCreation Methodを記述するところだが、今回はDataSetのインスタンスを返した方が他のTableModuleでも利用できるので、このようにした。
using System; using System.Data; using NUnit.Framework; using NUnit.Framework.SyntaxHelpers; using TableModuleSample.Core; namespace TableModuleSample.Tests { [TestFixture] public class OrderDetailTest { private OrderDetail target_; [SetUp] public void OrderDetailのインスタンス生成() { target_ = new OrderDetail(ObjectMother.CreateOrderByTsune()); } [Test] public void キーオブジェクトを指定して注文明細を特定できるべき() { DataRow orderDetail = target_[new OrderDetailKey(1, 2)]; Assert.That(orderDetail["OrderId"], Is.EqualTo(1)); Assert.That(orderDetail["ProductId"], Is.EqualTo(2)); Assert.That(orderDetail["UnitPrice"], Is.EqualTo(11000000m)); Assert.That(orderDetail["Quantity"], Is.EqualTo(20m)); Assert.That(orderDetail["Discount"], Is.EqualTo(0.3m)); } ... } }
次にObjectMotherのコード。Staticコンストラクタでデータセットのスキーマを作成し、あとはクローンでスキーマをコピーしたデータセットにテストデータを追加して最後にAcceptChangesを呼び出している点がポイント。
using System; using System.Data; namespace TableModuleSample.Tests { public class ObjectMother { private static readonly DataSet ds_; static ObjectMother() { ds_ = new DataSet(); SetSchema(); } private static void SetSchema() { DataTable products = new DataTable("Products"); products.Columns.Add("ProductId", typeof(int)); products.Columns.Add("ProductName", typeof(string)); ds_.Tables.Add(products); DataTable orders = new DataTable("Orders"); orders.Columns.Add("OrderId", typeof(int)); orders.Columns.Add("CustomerName", typeof(string)); orders.Columns.Add("OrderDate", typeof(DateTime)); orders.Columns.Add("ShippedDate", typeof(DateTime)); ds_.Tables.Add(orders); DataTable orderDetails = new DataTable("OrderDetails"); orderDetails.Columns.Add("OrderId", typeof(int)); orderDetails.Columns.Add("ProductId", typeof(int)); orderDetails.Columns.Add("UnitPrice", typeof(decimal)); orderDetails.Columns.Add("Quantity", typeof(decimal)); orderDetails.Columns.Add("Discount", typeof(decimal)); ds_.Tables.Add(orderDetails); } public static DataSet CreateOrderByTsune() { DataSet ds = ds_.Clone(); ds.Tables["Products"].Rows.Add(1, "ハマー"); ds.Tables["Products"].Rows.Add(2, "ファイアートラック"); ds.Tables["Products"].Rows.Add(3, "マスタング"); ds.Tables["Orders"].Rows.Add(1, "中西庸文", new DateTime(2007, 9, 1), new DateTime(2007, 9, 15)); ds.Tables["Orders"].Rows.Add(2, "中西庸文", new DateTime(2007, 9, 2), new DateTime(2007, 9, 16)); ds.Tables["OrderDetails"].Rows.Add(1, 1, 10000000m, 10m, 0.2m); ds.Tables["OrderDetails"].Rows.Add(1, 2, 11000000m, 20m, 0.3m); ds.Tables["OrderDetails"].Rows.Add(1, 3, 11500000m, 25m, 0.1m); ds.Tables["OrderDetails"].Rows.Add(2, 1, 10000000m, 10m, 0.2m); ds.Tables["OrderDetails"].Rows.Add(2, 2, 11000000m, 20m, 0.3m); ds.Tables["OrderDetails"].Rows.Add(2, 3, 11500000m, 25m, 0.1m); ds.AcceptChanges(); return ds; } } }
続いてTableModuleのスーパークラスのコード。
using System; using System.Data; namespace TableModuleSample.Core { public abstract class TableModule<KeyType> { protected DataTable table_; public TableModule(DataSet ds, string tableName) { this.table_ = ds.Tables[tableName]; } public DataRow this[KeyType key] { get { DataRow[] foundRows = table_.Select(FilterExpressionFrom(key)); if (foundRows.Length == 0) return null; else return foundRows[0]; } } public abstract string FilterExpressionFrom(KeyType key); public bool IsModified() { return table_.GetChanges() != null; } } }
最後にテスト対象のTableModuleクラスのコード。
using System; using System.Data; namespace TableModuleSample.Core { public class OrderDetail : TableModule<OrderDetailKey> { public OrderDetail(DataSet ds) : base(ds, "OrderDetails") { } public DataRow this[int orderId, int productId] { get { return this[new OrderDetailKey(orderId, productId)]; } } public override string FilterExpressionFrom(OrderDetailKey key) { return string.Format( "OrderId = {0} AND ProductId = {1}", key.OrderId, key.ProductId); } public void Insert(int orderId, int productId, decimal unitPrice, decimal quantity, decimal discount) { DataRow orderDetail = table_.NewRow(); orderDetail["OrderId"] = orderId; orderDetail["ProductId"] = productId; orderDetail["UnitPrice"] = unitPrice; orderDetail["Quantity"] = quantity; orderDetail["Discount"] = discount; table_.Rows.Add(orderDetail); } public void Update(int orderId, int productId, decimal unitPrice, decimal quantity, decimal discount) { DataRow orderDetail = this[orderId, productId]; orderDetail["UnitPrice"] = unitPrice; orderDetail["Quantity"] = quantity; orderDetail["Discount"] = discount; } public void DeleteGroupBy(int orderId) { DataRow[] foundOrderDetails = table_.Select( string.Format("OrderId = {0}", orderId)); foreach (DataRow foundOrderDatail in foundOrderDetails) { foundOrderDatail.Delete(); } } } public class OrderDetailKey { private int orderId_; private int productId_; public OrderDetailKey(int orderId, int productId) { this.orderId_ = orderId; this.productId_ = productId; } public int OrderId { get { return orderId_; } } public int ProductId { get { return productId_; } } } }