Tags: , , , | Categories: Code Development Posted by nurih on 2/10/2009 3:15 PM | Comments (0)

After seeing Pex in action at PDC 2008 I have caught the fever.

Since then, I gave it a whirle on my own and was pretty impressed. So much so, I chose it as a topic for one of my So-Cal Code Camp  talks in January. Got some very good questions and concerns regarding the capabilities and place of Pex in the world of software development and vis-a-vis TDD.

 

Tags: , , , | Categories: Code Development, Testing, Web Posted by nurih on 1/16/2009 10:25 AM | Comments (0)

Just came back from another great SoCal Code Camp. I had some valuable insights and discussions about TDD and the use of Pex. Thank you attendees!

While developing the presentation for Pex, I ran into a situation where the Pex.Assume() did not seem to work at all:

Consider the function

public List<short> MakeList(short baseNum, short count) 
{ 
List<short> result = new List<short>(count); 
for (short i = 1; i <= count; i++) 
{ 
result.Add((short)(baseNum * i)); 
} 
return result; 
}

 

Pex correctly identifies a potential flaw where the multiplication (baseNum * i) would result in overflow.

Adding a filter

PexAssume.IsTrue(baseNum * count < short.MaxValue); 

 

Seems like it would do the trick – but it doesn't.

Several rebuilds, clean solution, shake heads and searches for bugs later I found the issue: The predicate provided to PexAssume.IsTrue(predicate) produced an overflow! So when pex explores it would have tripped the condition I was trying to avoid by evaluating the parameters I tried to filter out.

The fix was to rewrite the filter as:

 

PexAssume.IsTrue(short.MaxValue / count > baseNum); 

 

Here, the math would not produce an overflow. Combined with PexAssume(count>0) and PexAssume(baseNum>0) my now filters work as (I) expected.

 

The take home is pretty obvious – ensure the predicate does not throw – but identifying it took a bit of head scratching.

Tags: , , , | Categories: Code Development, Testing, Unit Testing Posted by nurih on 8/23/2008 7:42 AM | Comments (0)

It is generally considered a good thing to use unit tests these days. Often it is necessary to test a method which takes some complex type. So in the unit testing one has to painstakingly manufacture such object, and pass it in.
Before doing so, you would (should!) ensure the complex type itself produces an identity - that is to say that if you create an instance of type MyClass and assign / construct it with proper values your should "get back" what you gave it. This is especially true for object that get serialized and de-serialized.

What I often do is use some helper code.
First snippet allows for testing an object for serialization using WCF, ensuring "round trip" serialization-de-serialization works.
The second snippet uses reflection to ensure that the object your put through the mill came back with identical values to the initial assigned one. This save a LOT of Assert.AreEqual(expected.PropA, actual.PropA) etc.
Since the object is actually a reference type, other equality checks would not do at the root level (such as ReferenceEuqals and the like).

Structs or nested structs are handled via ensureFieldsMatch() method.

 

Note that complex types may not be handled correctly – generics have not been addressed specifically here.

Future enhancements may include passing in an exclusion list of properties to skip or an inclusion list of properties to match exclusively. I'm on the fence on these, because the whole idea was to say "An object A matches B if every property and public fields match in value", and if one has to explicitly provide all property names one could just as well Assert.AreEqual(a.x, b.x) them.

Updated 2008-11-07: Error in comparison fixed. (Thank you  Rich for pointing it out!)

using System;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Nuri.Test.Helpers
{
public static class Equality
{
/// <summary>
/// Some properties are instance specific, and can be excluded for value matching (unlike ref equivalence)
/// </summary>
private static readonly string[] _ReservedProperties = { "SyncRoot" };
public static void EnsureMatchByProperties(this object expected, object actual)
{
ensureNotNull(expected, actual);
Type expectedType = expected.GetType();
Type actualType = actual.GetType();
Assert.AreEqual(expectedType, actualType);
if (expectedType.IsArray)
{
Array expectedArray = expected as System.Array;
Array actualArray = actual as System.Array;
Console.WriteLine(">>>*** digging into array " + expectedType.Name);
for (int i = 0; i < expectedArray.Length; i++)
{
Console.WriteLine("   ---   ---   ---");
EnsureMatchByProperties(expectedArray.GetValue(i), actualArray.GetValue(i));
}
Console.WriteLine("<<<*** digging out from array " + expectedType.Name);
}
else
{
ensurePropertiesMatch(expected, actual, expectedType, actualType);
}
}
public static void EnsureMatchByFields(this object expected, object actual, params string[] exclusionList)
{
ensureNotNull(expected, actual);
Type expectedType = expected.GetType();
Type actualType = actual.GetType();
Assert.AreEqual(expectedType, actualType);
if (expectedType.IsArray)
{
Array expectedArray = expected as System.Array;
Array actualArray = actual as System.Array;
Console.WriteLine(">>>*** digging into array " + expectedType.Name);
for (int i = 0; i < expectedArray.Length; i++)
{
Console.WriteLine("   ---   ---   ---");
expectedArray.GetValue(i).EnsureMatchByFields(actualArray.GetValue(i)); // recursion
}
Console.WriteLine("<<<*** digging out from array " + expectedType.Name);
}
else
{
ensureFieldsMatch(expected, actual, exclusionList);
}
}
private static void ensurePropertiesMatch(object expected, object actual, Type expectedType, Type actualType)
{
BindingFlags propertyExtractionOptions = BindingFlags.Public
| BindingFlags.NonPublic
| BindingFlags.Instance
| BindingFlags.Static
| BindingFlags.GetProperty;
foreach (PropertyInfo expectedProp in expectedType.GetProperties())
{
if (expectedProp.CanRead && !_ReservedProperties.Contains(expectedProp.Name))
{
if (expectedProp.PropertyType.IsValueType || expectedProp.PropertyType == typeof(String))
{
object expectedValue = expectedType.InvokeMember(expectedProp.Name,
propertyExtractionOptions,
null, expected, null);
object actualValue = actualType.InvokeMember(expectedProp.Name,
propertyExtractionOptions,
null, actual, null);
if (expectedValue == null && actualValue == null)
{
// both null - ok
Console.WriteLine("{0}: null == null", expectedProp.Name);
continue;
}
if (expectedValue == null || actualValue == null)
{
// one null the other not. Failure
Assert.Fail(expectedProp.Name + ": Expected Or Actual is null! (but not both)");
break;
}
Console.Write("{0}: {1} == {2} ?", expectedProp.Name, expectedValue.ToString(),
actualValue.ToString());
Assert.AreEqual(expectedValue, actualValue,
"Value of property doesn't match in " + expectedProp.Name);
Console.WriteLine(" true.");
}
else if (expectedProp.PropertyType.IsClass)
{
object expectedObject = expectedType.InvokeMember(expectedProp.Name,
propertyExtractionOptions,
null, expected, null);
object actualObject = actualType.InvokeMember(expectedProp.Name,
propertyExtractionOptions,
null, actual, null);
if (expectedObject != null
&& actualObject != null)
{
Console.WriteLine(">>>>>>>> digging into " + expectedProp.Name);
EnsureMatchByProperties(expectedObject, actualObject);
Console.WriteLine("<<<<<<<< back from dig of " + expectedProp.Name);
}
}
}
}
}
private static void ensureFieldsMatch(object expected, object actual, params string[] exclusionList)
{
Type expectedType = expected.GetType();
Type actualType = actual.GetType();
BindingFlags filedExtractionOptions = BindingFlags.GetField |
BindingFlags.NonPublic |
BindingFlags.Public |
BindingFlags.Instance;
foreach (FieldInfo expectedField in expectedType.GetFields(filedExtractionOptions))
{
if (!exclusionList.Contains(expectedField.Name))
{
if (expectedField.FieldType.IsValueType || expectedField.FieldType == typeof(String))
{
object expectedValue = expectedType.InvokeMember(expectedField.Name,
filedExtractionOptions,
null, expected, null);
object actualValue = actualType.InvokeMember(expectedField.Name,
filedExtractionOptions,
null, actual, null);
if (actual == null && expectedValue == null)
{
// both null - ok
Console.WriteLine("{0}: null == null", expectedField.Name);
continue;
}
if (expectedValue == null || actualValue == null)
{
// one null the other not. Failure
Assert.Fail(expectedField.Name + ": Expected Or Actual is null! (but not both)");
break;
}
Console.Write("{0}: {1} == {2} ?", expectedField.Name, expectedValue.ToString(), actualValue.ToString());
Assert.AreEqual(expectedValue, actualValue, "Value of filed doesn't match in " + expectedField.Name);
Console.WriteLine(" true.");
}
else if (expectedField.FieldType.IsClass)
{
object expectedObject = expectedType.InvokeMember(expectedField.Name,
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField, null, expected, null);
object actualObject = actualType.InvokeMember(expectedField.Name,
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField, null, actual, null);
if (expectedObject != null
&& actualObject != null)
{
Console.WriteLine(">>>>>>>> digging into " + expectedField.Name);
expectedObject.EnsureMatchByFields(actualObject);
Console.WriteLine("<<<<<<<< back from dig" + expectedField.Name);
}
}
}
}
}
/// <summary>
/// Ensures none of the values is null.
/// </summary>
/// <param name="parameters">The parameters to check for null.</param>
private static void ensureNotNull(params object [] parameters)
{
foreach( object obj in parameters)
if (obj == null)
{
throw new ArgumentNullException("at least one parameter is null");
}
}
}
}