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
}


Friday, October 17, 2008

Updating A Private Automatic Property

I had an issue at work today where I was trying to update a public get, private set automatic propety using reflection.

My first issue was I didn't now that when you reflect a type, you can access all the inherited public scoped methods and properties and fields, but you can't access any inherited private methods, properties, and fields. Once I had that figured out, I just had to figure out what the name of the field was.

I downloaded Red Gate's .Net Reflector (an awesome tool) and dissambled my sample test program. I then looked at the property generated by the compiler,

// Fields
[CompilerGenerated]
private long <CustId>k__BackingField;

// Properties
public long CustId

[
CompilerGenerated]
get
{
return this.<CustId>k__BackingField;
}
private [CompilerGenerated]
set
{
this.<CustId>k__BackingField = value;
}






And as you can quickly see, the name of the backing field for an automatic property is as follows, k__BackingField. Once I had the backing name figured out, I could update just like any other nonpublic field:

WinPart parent = new WinPart(_mainForm);
parent.GetType().GetField("<CustId>k__BackingField", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(this, newCustId);

Easily Determine Execution Time

Most everyone has gone through at one point in time or another, and tried to determine which of two statements, would be quicker to execute. Usually this involves doing two DateTime.Now() function calls and then doing a DateDiff, and having to search the internet for how to format the output correctly.

My coworker was exploring the System.Diagnostics namespace and stumbled upon the Stopwatch class which makes solving this issue much easier. Observe the snippet at the bottom of this post. Not that you'd ever want to do this particular example, but look at the power it gives you.

In this example, I'm wanting to see how long it takes to perform the GetEditLevel() function, while allowing the user to read the name of the customer. By starting the stopwatch only after the message box has been closed, and stopping it after the function has finished executing, it will keep track of the total number of ticks, then you can call ElapsedMilliseconds to get the total amount of time.

I think its a beautiful layer of abstraction.


int totalEditLevel = 0;
System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
foreach(CustomerInfo cust in CustomerList.GetCustomerListAll()){
if(watch.IsRunning){
//First time, Reset it
watch.Reset();
}
MessageBox.Show("Customer is " + cust.CustName);
watch.Start();
totalEditLevel += cust.GetEditLevel();
watch.Stop();
}
MessageBox.Show("Total Seconds was " + ((double)watch.ElapsedMilliseconds / 100.0).ToString());