OdeToCode IC Logo

ExpandoObject Explained In Tests (Except For One Mystery)

Monday, November 8, 2010

Update: Thanks to James and Samuel.  I’ve wrongly assumed that every test runner in the world re-instantiates the test fixture before executing each test method – but some do and some don’t. In this case I was using NUnit and the “mystery” is solved by adding an NUnit [Setup] method to create a fresh, new expando for each test run. Seems obvious, but only after ejecting all assumptions.

xUnit.net and the Visual Studio mstest tools DO create a fresh instance of the test fixture for each test method they execute inside the fixture. NUnit and MBUnit only create the fixture once, and as Jim Newkirk says:

I think one of the biggest screw-ups that was made when we wrote NUnit V2.0 was to not create a new instance of the test fixture class for each contained test method. I say "we" but I think this one was my fault. I did not quite understand the reasoning in JUnit for creating a new instance of the test fixture for each test method. I look back now and see that reusing the instance for each test method allows someone to store a member variable from one test and use it another. This can introduce execution order dependencies which for this type of testing is an anti-pattern.

The anti-pattern is demonstrated below. Smile

 

The following tests all pass for .NET 4.0’s ExpandoObject.

private dynamic expando = new ExpandoObject();

[Test]
public void Can_Add_A_Member()
{
    var expected = "Scott";            
    expando.Name = expected;
    
    Assert.AreEqual(expected, expando.Name);
}

[Test]
public void But_Cannot_Reflect_It()
{
    expando.Name = "Scott";

    Assert.IsNull(expando.GetType().GetProperty("Name"));           
}

[Test]
public void Is_A_Dictionary()
{
    var expected = "Scott";
    var dictionary = expando as IDictionary<string, object>;
    expando.Name = expected;

    Assert.AreEqual(expected, dictionary["Name"]);
}

[Test]
public void And_Also_Enumerable()
{
    var enumerable = expando 
             as IEnumerable<KeyValuePair<string, object>>;
    expando.Name = "Scott";

    Assert.IsTrue(enumerable.Any(kv => kv.Key == "Name"));
}

What’s The Mystery?

ExpandoObject implements INotifyPropertyChanged and the following test will pass.

[Test]
public void Will_Raise_PropertyChangedEvent()
{            
    dynamic expando = new ExpandoObject();
    var propertyName = "";
    ((INotifyPropertyChanged)expando).PropertyChanged +=
        (sender, args) => 
        {               
            propertyName = args.PropertyName;
        };
    expando.Name = "Scott";
    
    Assert.AreEqual("Name", propertyName);
}   

But, this version of the test fails unless you run under the debugger.

private dynamic expando = new ExpandoObject();        

[Test]
public void Will_Raise_PropertyChangedEvent()
{                        
    var propertyName = "";
    ((INotifyPropertyChanged)expando).PropertyChanged +=
        (sender, args) => 
        {               
            propertyName = args.PropertyName;
        };
    expando.Name = "Scott";
    
    Assert.AreEqual("Name", propertyName);
}   

In the passing test expando is local variable. In the failing test expando is a field. Scary.