views:

431

answers:

4

I have been writing unit tests using NUnit and Moq with my Silverlight code for some time now. One problem I keep running into has to do with DependencyObjects.

If anything is derived from DependencyObject, then I can't instantiate it in my test. For instance, MouseEventArgs derives from DependencyObject. If I have code that takes these args, I can't create the args for several reasons... one of them being that it is a DependencyObject.

As far as I understand, the base constructor of DependencyObject is trying to work with some statics that don't exist unless the entire Silverlight system is up and running. Any construction of a class that derives from DependencyObject throws an exception. Bummer.

I do not use the Silverlight Unit Test Framework, because it really isn't unit testing and requires a UI. I run need real, headless unit tests.

Anyways, The best I have come up with is to wrap these objects and give them interfaces like ITimelineMarker and I give them extension methods to do it: timelineMarker.ToInterface(). This works well, and I can mock them out... but I was wondering:

Has anyone come up with a better way to deal with DepencencyObjects in Silverlight Unit Tests?

A: 

Have you had a look at TestDriven.NETs Silverlight NUnit Project? Link

Graeme Bradbury
Yes. That is what we use. The problem happens in that setup.
Brian Genisio
A: 

I can see that DependencyObject in an abstract class.

Shamelessly ripping from Rhino mocks documentation, imagine you have an abstract class:

public abstract class MessageBase {
   private List<User> _receivers = new List<User>();
   public void Send() {
      //Some setup
      DetermineReceivers();
      SendMessages();
   }
   private void SendMessages() {
      //Lots of logic
   }
   protected abstract void DetermineReceivers();
   protected void AddReceiver(Group g) {
      //Lots of logic <---- Test this
   }
}

and you want to test the indicated part. Then you would create a fake class that you can mock:

TestFixture
public sealed class MessageBaseTester {
 public abstract class MockMessageMocker
 {
    public abstract List<Group> RecipientsGroups { get; set; }
 }
 private class MockMessage : MessageBase {
   private MockMessageMocker _mock;
   public MockMessage(MockMessageMocker mock) {
      _mock = mock;
   }
   protected override void DetermineReceivers() {
      if (_mock.RecipientsGroups != null)
         foreach(Group g in _mock.RecipientsGroups)
            base.AddReceiver(g);
   }
 }
}

Afterwards your test can do the following:

 Test
 public void ShouldblablablaWhenBlabla() {
    var SuThelper = mocks.Stub();
    var SuT = new MockMessage(SuThelper);
    using (mocks.Record())
    {
       Expect.Call(SuTHelper.RecipientsGroups).Return(
             new List<Group>{ new Group(...) });
    }
    using (mocks.Playback())
    {
       SuT.Send();
    }
 }

The above solution has an advantage over yours, in that you do not need to modify your original code. However you need to write some "extra" code for it to work.

drozzy
Unfortunately, this doesn't work. The objects I need to substitute are derived from DependencyObject (MouseEventArgs for example) which are often sealed and the constructor is internal :(
Brian Genisio
Sorry, but I don't see any MouseEventArgs in msdn that are derived from DependencyObject. Could you please provide a link?
drozzy
@drozzy: Doat! You're right... MouseEventArgs doesn't derive from DependencyObject. It does suffer from a very similar problem, though... it is a sealed class with an internal constructor. Some better examples (off the top of my head) are System.Windows.Media.SolidColorBrush or System.Windows.Media.TimelineMarker. Also, does Rhino Mocks work in Silverlight? This is just a nit... Moq works in SL and can mock out abstract classes. Even when I try it with a type I CAN derive from (System.Windows.Media.Brush), I still get the exception in the base constructor.
Brian Genisio
Sorry if I misunderstand (or don't know much about silverlight) but both of these classes have constructors of the form:public TimelineMarker()public SolidColorBrush()and neither of them seems to need any dependency objects during construction time. Could you provide a specific case or scenario which you want to test?
drozzy
They derive from DependencyObject. The base constructor for DependencyObject throws an exception when it constructs. The scenario is simple: construct a SolidColorBrush in a test. It throws.
Brian Genisio
?Why does the line in msdn where it says:SolidColorBrush mySolidColorBrush = new SolidColorBrush();works then?
drozzy
http://msdn.microsoft.com/en-us/library/system.windows.media.solidcolorbrush.aspx
drozzy
Ummmm... because that code isn't in a unit test? When you have a unit test, the code is run in isolation, outside of the browser. For whatever reason, the DependencyObjects can't be constructed in this environment. Any other code can execute, but DependencyObjects fail. You really just need to try it out. Create a silverlight project, create an NUnit test project using Graeme Bradbury's link, and write a test that creates a new SolidColorBrush. It will fail.
Brian Genisio
A: 

Try using SilverUnit for unit testing Silverlight. Since it's faking the Silverlight infrastructure, it may help with the problematic exceptions.

Unfortunately, that uses the TypeMock isolator, which is not my cup of tea. I have thought about that, but I would prefer a better way to make my classes less dependent on system objects.
Brian Genisio
+3  A: 

Why do you have application logic tied directly to SilverLight events and objects? The caveats of trying to test an application's logic through it's UI are well known anti-patterns and have given way to patterns that solve this problem in a more effective manner.

Your UI really should only be concerned with UI events and pass any processing to another layer, the only thing then left to test in your UI would be graphical responses (such as when you click a button does a panel slide in from the side), and the only good way to test interactions like that is to actually do the clicking.

Perhaps a more appropriate answer to your question would be that you need to refactor your code to use a patter such as MVC, MVP, or MVVM so that you can test your application logic independently of your UI layer.

joshperry
+1 That is a great question... and it turns out that for the most part, this is NOT a problem for me... I use the MVVM pattern whenever I can and it is great. But creating that separation causes some logic to be in the code-behind still. For instance... lets assume that I have a bunch of timeline markers in a video. I want to process those markers in my application logic. I can extract them in the code-behind and pass them into my logic and process them. They are really just data. The problem with doing that is the timeline marker derives from DependencyObject.
Brian Genisio
Continued: In every case that I find that I want to be working against DependencyObjects, (it is almost always plain-old-data style classes) my approach has been to wrap the object in a proxy that implements a similar interface. It allows my MVVM pattern to continue working well.My goal here, is to get as much as possible out of the code-behind... if it is in the code-behind, it can't be tested. Some data objects that derive from DependencyObject foil my plans :)If there were a way to construct these objects, it would simplify my code.Good answer, though.
Brian Genisio
I have some similar paradigms in our application and what I do is make what you could call a subview. Say you have a TimelineViewModel that has a collection of TimelineMarkerViewModels, I would create a User Control and a DataTemplate that uses that control to be the "view" for a timeline marker. This makes binding very simple, and if you use some implementation of ICommand on your ViewModel you can even bind actions with no code behind. I create a ViewModel for objects that need interaction in the UI, if I'm only to displaying it's data then I feel fine binding directly to the model entity.
joshperry