Friday, October 31, 2008

Automated CSLA Testing Helper

I was trying to come up with my first test project using the Tester built into VS 2008. I wanted to be able to do some basic CRUD testing on my Csla Business Objects.  I quickly realized that I didn't want to have to go through and set all of my properties by hand for every single class.  Even if I had all the time in the world to do that, if they changed the database schema, which would result in me having to re-code-gen the BOs, I would have to edit my test scripts to handle any new columns (which I would always forget) and remove any columns that no longer exist.  What a pain.

Enter Reflection.  I was able to create a generic SetPropertyValues function that loops through all the properties of any BusinessObject and automatically assigns all them all a different value.  
This is how it works:
    1. Using reflection, I loop through all the properties on the BO with a counter that is incremented each time
    2. I look at the type of the property and assign it a value based off of its type, using the counter as a base
    3. I then save the value off to a generic list of PropertyInfo, and string values to compare with later.
Thats it.

I then wraped that funciton with two other functions, one to perform the intial insert, and one with a different seed value, to perform an update test.  Below is the .Net test class I setup for my customer class, and the SetPropertyValues function that is used to simplify the test.

Enjoy.




using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace BusinessObjectsUnitTest {
static class G2TestHelper {

public static List<PropertyCallerValuePair> SetInitialPropertyValues<T>(Csla.BusinessBase<T> target) where T : Csla.BusinessBase<T> {
return SetPropertyValues(target, 0);
}

public static List<PropertyCallerValuePair> SetUpdatedPropertyValues<T>(Csla.BusinessBase<T> target) where T : Csla.BusinessBase<T> {
return SetPropertyValues(target, 1);
}

private static List<PropertyCallerValuePair> SetPropertyValues<T>(Csla.BusinessBase<T> target, int intialValue) where T : Csla.BusinessBase<T> {
List<PropertyCallerValuePair> values = new List<PropertyCallerValuePair>();
string stringValue = string.Empty;

foreach (PropertyInfo property in target.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly).Where(p => p.CanWrite && !(p.PropertyType is Csla.Core.IBusinessObject))) {

if(typeof(string).IsAssignableFrom(property.PropertyType)){
stringValue = intialValue.ToString();
property.SetValue(target, stringValue, null);
}
else if (typeof(bool).IsAssignableFrom(property.PropertyType)) {
}
else if (typeof(long).IsAssignableFrom(property.PropertyType) ||
typeof(int).IsAssignableFrom(property.PropertyType) ||
typeof(decimal).IsAssignableFrom(property.PropertyType)) {
stringValue = intialValue.ToString();
property.SetValue(target, intialValue, null);
}
else if (typeof(Csla.SmartDate).IsAssignableFrom(property.PropertyType)) {
Csla.SmartDate time = new Csla.SmartDate(DateTime.Now.AddDays(intialValue));
stringValue = time.ToString();
property.SetValue(target, time, null);
}
else if (typeof(DateTime).IsAssignableFrom(property.PropertyType)){
DateTime time = DateTime.Now.AddDays(intialValue);
stringValue = time.ToString();
property.SetValue(target, stringValue, null);
}
else {
throw new Exception("Unrecognized Type");
}

values.Add(new PropertyCallerValuePair(property, stringValue));
intialValue++;
}

return values;
}

public static void AssertAreEqual<T>(Csla.BusinessBase<T> target, List<PropertyCallerValuePair> values) where T : Csla.BusinessBase<T> {
foreach (PropertyCallerValuePair property in values) {
Assert.AreEqual(property.Value, property.Property.GetValue(target, null).ToString());
}
}
}

public class PropertyCallerValuePair
public PropertyInfo Property { get; set; }
public string Value { get; set; }

public PropertyCallerValuePair(PropertyInfo property, string value) {
Property = property;
Value = value;
}
}
}






Hear is the extremely simple test script would could easily be code generated:
        /// <summary>
///A test for Customer CRUD
///</summary>
[TestMethod()]
public void CustomerCRUD() {
//Begin Create
Customer expected = Customer.NewCustomer();
List<PropertyCallerValuePair> values = G2TestHelper.SetInitialPropertyValues(expected);
Customer actual = expected.Save();
//Test Create
G2TestHelper.AssertAreEqual(actual, values);
//End Create

expected = actual;

//Begin Read
actual = Customer.GetCustomer(expected.CustId);
//Test Read
G2TestHelper.AssertAreEqual(actual, values);
//End Read

expected = actual;

//Begin Update
values = G2TestHelper.SetUpdatedPropertyValues(expected);
expected = expected.Save();
G2TestHelper.AssertAreEqual(expected, values);
actual = Customer.GetCustomer(expected.CustId);
//Test Update
G2TestHelper.AssertAreEqual(actual, values);
//End Update

expected = actual;

//Begin Delete
Customer.DeleteCustomer(expected.CustId);
try {
actual = Customer.GetCustomer(expected.CustId);
}
catch {
actual = null;
}
//Test Delete
Assert.IsNull(actual);
//End Delete
}


1 comment:

Anonymous said...

Thank you a lot for tutorial. I enjoy research of your blog. I learn to use System.Reflection and is very helpful. Wow unit testing is much powerful. Old code is slow and bulky like your mom, new code fast and nice looking like your sister.
much reguard
- Manish Poobla
in Asansol India