Showing posts with label CSLA. Show all posts
Showing posts with label CSLA. Show all posts

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, September 5, 2008

Using LINQ To Ensure No Objects Have Duplicate Keys

I had an interseting business requirement the other day. I'm working with a CSLA Collection of objects, and they wanted the end user to be able to be able to change a property that is one of the three columns that makes up the primary key. I sat down and started using some LINQ to objects and was amazed at how much power it has in just a 6 lines of code. It was also my first time using the Group By Function

Normally with the group by, you perform some agregate functions on the group. So the "Into Group" would be "Into Sum(naviagation.SectDeNavOrdrNbr)". But by Grouping it into group, the result is a collection that contains the objects in the Group By Clause (SectDeNavOrdNbr, SectName, DeName) and then a collection of all the objects that are in that group.

So after I have my group by clause, I perform a contains to see if any collections of the group have more than one item in them. The must amazing part, is techinally, it only took 1 line of code! In the words of a dear friend of mine, "God Bless .Net".




Dim hasDuplicatePrimaryKeys As Boolean = ( _
From navigation As SectionDataElementNavigationReference In Me _
Group By navigation.SectDeNavOrdNbr, navigation.SectName, navigation.DeName _
Into Group).Contains(Function(c) c.Group.Count() > 1)

If (hasDuplicatePrimaryKeys) Then
Throw New InvalidConstraintException("More than one Section Data Element Navigation Reference was found with the same Section Name, Data Element Name, and Section Data Element Navigation Order Number")
End If