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. ![]()
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")); }
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.
Comments
I've had it fail both with the R# test runner and the NUnit test runner on two different machines.
I wonder what the difference could be.
There's a straightforward explanation: Reflector it, and you'll see that ExpandoObject only raises PropertyChanged events when the value actually changes, not when you re-assign the same value as before.
Since your other tests are using the same ExpandoObject instance, and assigning the same value of "Scott" to the Name property, no event is being raised. Change the failing test to use a different property, or give it a different value, and it all works!
I guess it all depends on whether your test framework creates a new instance of the Test Fixture class before running each test.