Printed from www.rmfusion.com A Developer website designed for Developers

NUnit Mock with WinForms Code Review

Code Download

* Based on NUnit version 2.4.8

Define the DataAccess Interface and Handler Object

Define the IShoppingDataAccess interface, which defines any actions that can be performed by the application classes.

public interface IShoppingDataAccess { String GetProductName(Int32 productID); Int32 GetUnitPrice(Int32 productID); List<BasketItem> LoadBasketItems(Guid basketID); void SaveBasketItems(Guid basketID, List<BasketItem> basketItems); }

These actions will usually involve querying or updating one or more databases.

Define the DataAccessProvider class, which inherits from the IShoppingDataAccess interface, and defines the relevant data access methods.

class DataAccessProvider : IShoppingDataAccess { public string GetProductName(int productID) { return (DataStoreAccessor.FindProductNameByProductID(productID)); } public int GetUnitPrice(int productID) { return (DataStoreAccessor.FindUnitPriceByProductID(productID)); } public List<BasketItem> LoadBasketItems(Guid basketID) { return(DataStoreAccessor.RetrieveItemsByBasketID(basketID)); } public void SaveBasketItems(Guid basketID, List<BasketItem> basketItems) { DataStoreAccessor.SaveItemsByBasketID(basketID, basketItems); } }

Define the DataStoreAccessor class, which is simply a static class which is used as a substitue for an actual database.

static class DataStoreAccessor { private static Dictionary<Guid, List<BasketItem>> m_savedBasket = new Dictionary<Guid, List<BasketItem>>();
public static String FindProductNameByProductID(Int32 productID) { String productName = String.Empty;
switch (productID) { case 1: productName = "The Moon"; break;
case 5: productName = "Love"; break; } return (productName); } public static Int32 FindUnitPriceByProductID(Int32 productID) { Int32 unitPrice = Int32.MinValue;
switch (productID) { case 1: unitPrice = 99; break;
case 5: unitPrice = 47; break; } return (unitPrice); } public static List<BasketItem> RetrieveItemsByBasketID(Guid basketID) { return (m_savedBasket[basketID]); } public static void SaveItemsByBasketID(Guid basketID, List<BasketItem> basketItems) { List<BasketItem> tmpList = new List<BasketItem>(); tmpList.AddRange(basketItems);
if (m_savedBasket.ContainsKey(basketID)) { m_savedBasket.Clear(); } m_savedBasket.Add(basketID, tmpList); } }

The point of this class is to return "hard-coded" data based on certain class method calls. Using this class also means the application can run without needing to connect to a database data source.

In reality, this class would not be required and all method calls in the DataAccessProvider class would connect to a database directly and the results persisted from the database itself.

This class is only used when running the application normally. When running the unit tests and mocking objects, this class will not be invoked.

Define the Basket and BasketItem Objects

The Basket class invokes the LoadBasketItems and SaveBasketItems methods of the IShoppingDataAccess interface.

public class Basket { private List<BasketItem> m_basketItems; private Guid m_basketID; private IShoppingDataAccess m_dataAccessProvider;
public Basket() { this.m_dataAccessProvider = new DataAccessProvider(); this.m_basketItems = new List<BasketItem>(); this.m_basketID = Guid.NewGuid(); } public Basket(IShoppingDataAccess dataAccessProvider) { this.m_dataAccessProvider = dataAccessProvider; this.m_basketItems = new List<BasketItem>(); this.m_basketID = Guid.NewGuid(); }
public void AddItem(BasketItem item) { this.m_basketItems.Add(item); } public Decimal CalculateSubTotal() { Decimal subTotal = 0; foreach (BasketItem item in this.m_basketItems) { subTotal += item.GetPrice(); } return (subTotal); } public List<BasketItem> Load() { return (this.m_basketItems); } public void Save() { this.m_dataAccessProvider.SaveBasketItems(this.m_basketID, this.m_basketItems); } }

Define the BasketItem class, which represents a class to be unit tested.

public class BasketItem { private Int32 m_productID; public String ProductName { get; private set; } public Decimal Quantity { get; set; } public Decimal UnitPrice { get; private set; }
private IShoppingDataAccess m_dataAccessProvider;
public BasketItem(Int32 productID, Int32 quantity) { this.m_dataAccessProvider = new DataAccessProvider(); this.ProductID = productID; this.Quantity = quantity; } public BasketItem(Int32 productID, Int32 quantity, IShoppingDataAccess dataAccessProvider) { this.m_dataAccessProvider = dataAccessProvider; this.ProductID = productID; this.Quantity = quantity; }
public Int32 ProductID { get { return this.m_productID; } set { this.m_productID = value; this.UnitPrice = this.m_dataAccessProvider.GetUnitPrice(this.m_productID); this.ProductName = this.m_dataAccessProvider.GetProductName(this.m_productID); } }
public Decimal GetPrice() { return (this.UnitPrice * this.Quantity); } }

Define two constructors; first a default constructor with two input parameters and a second constructor accepting the IShoppingDataAccess interface type as an input parameter. The second constructor will be used by the mocking objects when running the unit tests.

The BasketItem class invokes the GetProductName and GetUnitPrice methods of the IShoppingDataAccess interface.

Define the Object Test Fixture Class

Define the Test class, which contains all the unit test code to be run against the Basket and BasketItem classes.

[TestFixture] public class TestBasketItem { private DynamicMock m_dataAccessProvider = null;
[TestFixtureSetUp] public void FixtureInitialise() { Console.WriteLine("FixtureInitialise"); }
[SetUp] public void TestInitialise() { this.m_dataAccessProvider = new DynamicMock(typeof(IShoppingDataAccess)); this.m_dataAccessProvider.Strict = false; Console.WriteLine("TestInitialise: {0}", (this.m_dataAccessProvider == null)); }
[Test] public void TestSetReturnValueOfUnitPriceAndProductName() { this.m_dataAccessProvider.SetReturnValue("GetUnitPrice", Convert.ToInt32(99)); this.m_dataAccessProvider.SetReturnValue("GetProductName", "The Moon");
Console.WriteLine("TestSetReturnValueOfUnitPriceAndProductName: {0}", (this.m_dataAccessProvider == null));
BasketItem item = new BasketItem(1, 2, (IShoppingDataAccess)this.m_dataAccessProvider.MockInstance); Assert.That(99, Is.EqualTo(item.UnitPrice)); Assert.That("The Moon", Is.EqualTo(item.ProductName)); Assert.That(198, Is.EqualTo(item.GetPrice()));
this.m_dataAccessProvider.Verify(); }
[Test] public void TestExpectAndReturnOfUnitPriceAndProductName() { this.m_dataAccessProvider.ExpectAndReturn("GetUnitPrice", 99, 1); this.m_dataAccessProvider.ExpectAndReturn("GetProductName", "The Moon", 1); this.m_dataAccessProvider.ExpectAndReturn("GetUnitPrice", 47, 5); this.m_dataAccessProvider.ExpectAndReturn("GetProductName", "Love", 5);
Console.WriteLine("TestExpectAndReturnOfUnitPriceAndProductName: {0}", (this.m_dataAccessProvider == null));
Basket b = new Basket((IShoppingDataAccess)this.m_dataAccessProvider.MockInstance); b.AddItem(new BasketItem(1, 2, (IShoppingDataAccess)this.m_dataAccessProvider.MockInstance)); b.AddItem(new BasketItem(5, 1, (IShoppingDataAccess)this.m_dataAccessProvider.MockInstance));
b.Save(); Decimal subTotal = b.CalculateSubTotal(); Assert.That(245, Is.EqualTo(subTotal));
this.m_dataAccessProvider.Verify(); }
[Test] public void TestExplicitExpectAndReturnOfUnitPriceAndProductName() { Basket basket = null; BasketItem item1 = null; BasketItem item2 = null; List<BasketItem> itemsCol = null;
this.m_dataAccessProvider.Strict = true;
this.m_dataAccessProvider.ExpectAndReturn("GetUnitPrice", 99, 1); this.m_dataAccessProvider.ExpectAndReturn("GetProductName", "The Moon", 1); this.m_dataAccessProvider.ExpectAndReturn("GetUnitPrice", 47, 5); this.m_dataAccessProvider.ExpectAndReturn("GetProductName", "Love", 5);
Console.WriteLine("TestExplicitExpectAndReturnOfUnitPriceAndProductName: {0}", (this.m_dataAccessProvider == null));
item1 = new BasketItem(1, 2, (IShoppingDataAccess)this.m_dataAccessProvider.MockInstance); item2 = new BasketItem(5, 1, (IShoppingDataAccess)this.m_dataAccessProvider.MockInstance); itemsCol = new List<BasketItem>() { item1, item2 };
this.m_dataAccessProvider.Expect("SaveBasketItems", Is.TypeOf(typeof(Guid)), itemsCol);
basket = new Basket((IShoppingDataAccess)this.m_dataAccessProvider.MockInstance); basket.AddItem(item1); basket.AddItem(item2);
basket.Save();
Decimal subTotal = basket.CalculateSubTotal(); Assert.That(245, Is.EqualTo(subTotal));
this.m_dataAccessProvider.Verify(); }
[Test] public void TestConfigExpectAndReturnOfUnitPriceAndProductName() { Basket basket = null; BasketItem item1 = null; BasketItem item2 = null; List<BasketItem> itemsCol = null;
this.m_dataAccessProvider.Strict = true;
this.m_dataAccessProvider.ExpectAndReturn("GetUnitPrice", 99, 1); this.m_dataAccessProvider.ExpectAndReturn("GetProductName", "The Moon", 1); this.m_dataAccessProvider.ExpectAndReturn("GetUnitPrice", 47, 5); this.m_dataAccessProvider.ExpectAndReturn("GetProductName", "Love", 5);
Console.WriteLine("TestExplicitExpectAndReturnOfUnitPriceAndProductName: {0}", (this.m_dataAccessProvider == null));
item1 = new BasketItem(1, 2, (IShoppingDataAccess)this.m_dataAccessProvider.MockInstance); item2 = new BasketItem(5, 1, (IShoppingDataAccess)this.m_dataAccessProvider.MockInstance); itemsCol = new List<BasketItem>() { item1, item2 };
this.m_dataAccessProvider.Expect("SaveBasketItems", Is.TypeOf(typeof(Guid)), itemsCol);
basket = new Basket((IShoppingDataAccess)this.m_dataAccessProvider.MockInstance); basket.AddItem(item1); basket.AddItem(item2); basket.Save();
Decimal subTotal = basket.CalculateSubTotal(); Assert.That(245, Is.EqualTo(subTotal));
this.m_dataAccessProvider.Verify(); }
[TearDown] public void TestTearDown() { this.m_dataAccessProvider = null; Console.WriteLine("TestTearDown: {0}", (this.m_dataAccessProvider == null)); }
[TestFixtureTearDown] public void FixtureTearDown() { Console.WriteLine("FixtureTearDown"); } }

A dynamic mock object is created, which mimics method calls to the DataAccessProvider class. The actual DataAccessProvider class is not invoked here, but rather a mock instance containing the methods defined in the IShoppingDataAccess interface.

Four tests are defined in this Test class, with appropriate Setup and TearDown methods at both test and class levels. Debug messages are written to the console for illustration purposes only.

Define the Form Test Fixture Class

Define another Test class to unit test the Windows Form application class.

[TestFixture] public class TestFormShopping : NUnitFormTest { Form1 FormShopping;
public override void Setup() { FormShopping = new Form1(); FormShopping.Show(); }
[Test] public void TestFormAddToBasketButton() { TextBoxTester textbox = new TextBoxTester("txtSubTotal"); ButtonTester button = new ButtonTester("btnAddBasket"); ListBoxTester listbox = new ListBoxTester("lstProducts"); ControlTester numQty = new ControlTester("numQty");
Console.WriteLine("Add Product 1 to Basket"); listbox.SetSelected(0, true); button.Click();
Console.WriteLine("Add Product 5 to Basket"); listbox.SetSelected(1, true); numQty["Value"] = Convert.ToDecimal(3); button.Click();
Assert.AreEqual(textbox.Text, "240"); } }

This class inherits from the NUnitFormTest class and defines a test method to automatically test adding items to a basket.