index > Windows Presentation Foundation ("Avalon") > How do I force a template to be instantiated in a test?

How do I force a template to be instantiated in a test?

I'm writing unit tests for view classes in a WPF application I'm working on. I want the test to verify that the view will invoke certain methods on my viewmodel class when it is fed certain input.

I've run into a problem: unless I actually open up a window to host and show the view, templates used inside the view don't appear to be expanded. For example, the way the view presents certain items from my model is to create a ContentControl with its Content property set to refer to an item from the model, and with its ContentTemplate set to a template that will display the item from the model in the way that I want.

I'm trying to simulate user input such as mouse events by calling RaiseEvent directly on elements in the UI. This works fine if I raise them directly on the ContentControl, but when the app is actually running the mouse input will be directed to the visuals supplied by the template. (The ContentControl itself has no intrinsic visuals, and it's using its default control template, which I believe is just a ContentPresenter. So the only thing you can click on will be the instantiated content template.)

You can use VisualTreeHelper to locate the items supplied by the template, and raise events directly from those. This works, but I have to create and Show a window first. If I don't, the visual tree doesn't seem to contain the expanded template.

So I'm wondering if there's something I can do to trigger the expansion of the templates during a test other than just showing the UI. Having windows pop open and close again when running tests is kind of ugly. I'm also not sure what'll happen on a build server if you run tests that try to show the UI...

IanG

D'Oh! Looks like I've been fooled yet again by VSTS's unit testing running an old copy of my code rather than the new version I just built. *sigh*

It turns out that one of the things I tried and thought had failed does actually work:

view.Width = 10;

IanG

I don't actually know the answer to your question, but I think you are performing the wrong tests.

In my opinion you have 2 ways of testing this without having to test the view:

  1. First, you have to use the MVC or MVP design pattern and test only the Controler or the Presenter;
  2. Then, the first approach would be to call the public methods on the Presenter or the Controler and test their results, since these are the ones that call the View or act on the View's events;
  3. The other way would be to keep your design and derive your test from the the View class (.cs) and test it by calling the methods directly, since you would have access to all the methonds (even the protected ones) once you are on a derived class.

You may want to take a look at Martin Fowler's site.

Hope this helps.




Z4
Duque Vieira

I am using a design pattern that separates out the logic from the view. (I'm using Model/View/ViewModel rather than MVC or MVP. MVC in its original sense no longer makes any sense in modern UIs since so much of what used to be in the controller has now been subsumed into the frameworks. MVV is about the closest thing there is to MVC that actually makes any sense in WPF. However, people mean so many different things by MVC these days, that for some definitions, MVC and MVV are pretty much the same thing. So depending on which of the several hundred extant definitions of MVC you have in mind, I may be doing what you consider to be MVC.)

In short, my view doesn't know how to do anything. It just tells the viewmodel what's happening, and does what the viewmodel tells it to do.

And I am already testing the model and the viewmodel in isolation, thanks... Everything that can be tested without the view being involved is being tested without plugging in a view. But the view still has to be there in the app - I'm building a UI here!

Your suggestion that I don't test the view as well seems defeatist, and rather unhelpful. Moreover, I recognize it as the stock answer given by unit testing experts to unit testing newbies, and it's an answer I believe to be unsatistfactory, misguided, and actively misleading. I'm not actually the newbie you appear to think I am - I've been building user interfaces with TDD for about 4 years now... (So I'm also familiar with Martin Fowler's site thanks...)

I understand the benefits of separating out as much as you can from the view so that you can test all the interesting logic in isolation wherever possible. But the fact remains that no matter how much you separate out, the view still has some work to do. It should be as little as possible, but it'll never go down to nothing.

In the project in question, the code in my view so far consists entirely of trivial event handlers each containing just one line of code that calls the appropriate method on the viewmodel, and short methods called by the viewmodel that let it manipulate elements in the view. The view never decides what to do with user input, nor does it decide what to display - these decisions are all managed by the viewmodel. But the view still has to exist, because something has to talk to WPF in order to handle events, and manipulate the objects that form the actual UI.

You're never going to be able to get rid of that kind of code entirely. The view has to handle the raw UI events and forward them to the viewmodel. The view has to manipulate the raw UI elements in response to the requests made by the viewmodel. After all, this is the purpose of the view: to mediate between whatever UI technology you're using (WPF in this case), and the logic that determines what the application does.

This mediation is a critical piece of code - without it the app won't have a UI. And it's code that intrinstically can only be in the view, as it involves direct interaction with the WPF-specific nuts and bolts that make up the UI. I think you'd agree that building any WPF-specific code into my model or viewmodel would be a mistake.

Since this is important code, I want to test it. And for the most part I'm able to test it - everything in my views thus far in this project has been written test-first. (Something I wouldn't have achieved if I'd followed the approach you appear to be recommending, and only tested the non-view code.)

Since I can write unit tests for the view, I believe I should. (Even if they are occasionally inelegant. I'd still rather have a slightly clunky test than no test at all.) I want the interface between my view and my viewmodel to enjoy the design benefits TDD brings to the rest of the system - I believe this boundary is better than it would have been if I hadn't been using TDD. (Or if I'd only been using TDD in one direction - obviously you can mock the view when testing the viewmodel. But the view/viewmodel interface is intrinsically bidirectional, so I think it's valuable also to mock the viewmodel and test the view.)

Just to be clear, the view tests are all pretty simple. They tend to be of the form "When this kind of user input occurs, the view notifies the viewmodel" or "When the viewmodel asks for a new item in the model to be displayed by the view, we can see a new UI element appearing in the relevant place, databound to the relevant item." I'm not testing application behaviours here, not even fairly low-level UI interactions. For example, tests of the form "When the mouse button goes down and the mouse them moves a certain distance, visual feedback indicating that a drag operation is now in progress is generated" are in the viewmodel's tests, not the view's. All I'm really testing on the view is that it provides the plumbing it's supposed to provide, enabling the viewmodel to do its job of running the UI interactions.

So I reject your suggestion that I should give up on trying to test my view, and just test the other parts of my UI. I've so far been pretty successful with TDD on the view, and I don't wish to give that up.

IanG

I'm sorry that you found my answer offensive, that was not really my intention and I appologise if it suggested so.

I agree with you entirelly and it's very good that you took the time to explain it so well.

In trying to solve your problem, I have another suggestion:

  1. Maybe you could just check the view's template property to see if the tree has the nodes you expected it to have. This way you don't have to raise events;
  2. Then, to check just the template, you could create a separate class with just the template and test it in an isolated manner;
  3. The usage would work by injecting an instance of the template classe into the view's template property.

What do you think?

Best regards.




Z4
Duque Vieira

I didn't find it offensive - I just got the impression that you had slightly misjudged where I was coming from. Sorry if my reply came across as aggressive!

For (1), that's fine for some contexts, but doesn't really help in the context of the problem I'm trying to solve. It's not the presence of the template the I'm checking for. (I have another test that does that. So the suggestion is helpful in a broader sense - that test works in exactly the way you suggest. But it doesn't solve the problem I'm dealing with here.) I'm specifically testing for how the view responds to user input. The relationship between the control and its template is an important part of this, because the template has an impact on how the control receives user input - events arrive in the template and bubble up into the control, so if you don't have both parts present, you've not got a representative test of user input handling. For some tests you can separate these two aspects, but for other tests, the combination of control and template is important.

I have a simple test that works just fine, where I fire the event directly into the ContentControl itself. That works, but my main problem with that is that it's not representative of how events are really raised. So while it does a basic plumbing sanity check, it's not a very good test in that it's not a very accurate reflection of how it'll be used in practice.

I would prefer to have a test that is as much like real user input as possible. And in reality, mouse input will initially arrive at the elements in the visual tree formed by the control's template, and will bubble up to the ContentControl itself.

Besides wanting my test to reflect as accurately the way in which the class will be used in reality, I have a couple of other reasons for wanting to check this Control+ContentTemplate scenario. One is to make sure that mouse coordinate handling works correctly. (It's fairly easy to envisage a bug where the view reports mouse positions relative to the wrong UI element.) Another is to make sure that the view correctly conforms to the protocol by which the viewmodel expects view elements to be identified. (Without going into too much detail, the viewmodel needs to be able to work out the correspondance between incoming events and objects in the model. It'd be fairly easy to introduce a bug where the view sends what the viewmodel expects when the view is handling events that were fired directly at the controls it created, but not when the events came in via children of that control generated by the template.)

On (2), I'm doing this already in some tests. I use the LoadContent method to instantiate the template when I want to test aspects that are purely part of the template. But the issue that led to my original post here is that I need to test the interaction between the template and is containing control. So I really do need to force the template to be instantiated in situ. (Either that, or I need to create a test environment which completely and accurately simulates everything that WPF would do when instantiating the template for real. That seems hard, particularly since I don't think there's enough documentation to do it correctly, and I'm not sure it'd be that valuable. While it would remove the need to solve the problem I've posed, it seems to replace it with a set of much harder problems; I think the hack I've shown above is probably preferable, and certainly easier.)

On (3), that's not how templates work. The ContentTemplate is of type DataTemplate. You can't inject an instance of the template class into the view just by setting the ContentTemplate property. (And likewise with control templates, you can't inject an instance of the control's template class into the view by setting the Template property.) All you're doing there is telling it which template you'd like it to instantiate when the time comes to instantiate it. You still need to do something to get the control to use that Template or ContentTemplate property. (That's the very thing I'm asking how to do!)

Also, there isn't actually a specific template class for one thing - a content template is an instance of the DataTemplate class. So although WPF templates feel a bit like classes - a reusable prototype that gets instantiated in each place it is used - the implementation is actually very different. A template is an object, not a class. It's an instance of some type derived from FrameworkTemplate. (DataTemplate for data templates, ControlTemplate for control templates, and ItemsPanelTemplate for the template for an ItemsControl's item container panel.) Its VisualTree property contains a FrameworkElementFactory, which contains instruction on how to build an instance of the template.

So it's basically a factory object. And there isn't a distinct type associated with any particular template. If I have three data templates in my project, they all have the same type (DataTemplate). So that's three distinct templates by only one type. So there is no distinct "template class" corresponding to a particular template.

And while you can use LoadContent to instantiate a template (i.e. build a visual tree as described by the template's VisualTree property), this is not actually the same thing as using the template in context. If you get WPF to apply the template for you, the behaviour is typically more complex and subtle than simply calling LoadContent.

For example, if you provide a DataTemplate for a TreeView, WPF will not just instantiate your template. It will also wrap it in a TreeViewItem container for you. And it will set the DataContext for you. And then there's the matter of triggers - I don't even know how those are wired up internally. And it probably does other things I'm not aware of.

So if you try to instantiate the thing manually in a test where the relationship between the template instance and the templated control matters to you, you've got the problem of working out how to duplicate all of WPF's behaviour in that context. Which is why I'd much rather just let WPF instantiate the thing for me in the context in which it's meant to be used: that way I know that I'll be getting all the normal behaviour WPF offers because WPF is doing the work for me.

So that's why I'm trying to work out what the easiest way of getting WPF to instantiate the thing for me in situ is.

IanG

OK Ian, point taken.

I wish you the best of luck in your test.

If you find out how to do it, please tell us.

Best regards.




Z4
Duque Vieira

You can force a template to be instantiated (applied to the element) with the ApplyTemplate method.

That only goes one level deep, though. For example, if you have this:

<ListBox>
<Button/>
</ListBox>

... calling ApplyTemplate on the ListBox will expand its template, but not the button's template, or the ScrollViewer's template (the ScrollViewer is part of the ListBox template). So after ApplyTemplate on an element, you need to use VisualTreeHelper to walk the visual tree and recursively ApplyTemplate any FrameworkElement type object that you find within.

- Mike

Mike Hillberg - MSFT

Wow - am I embarrassed? I wonder how I managed to miss that!

Thanks!

IanG
reply 9

You can use google to search for other answers

 

More Articles

• Animating a ListBox Scrolling Text
• Not able to get "A Visual" from VisualCollection object...
• Cannot re-initialize ResourceDictionary instance. Error at object...
• How does WPF Handle simple control focus?
• WindowsFormsHost and AllowsTransparency="True" conflict...
• Post to delete
• Moving FrameworkElement using DoubleAnimation in code?
• Editable TreeView
• ScaleTransform a Visual to create reflection effect
• How to put 2 ListBoxItem on same row?
Bookmark and Share
Welcome to Bokebb   New Update  
 

New Articles

• Update UI Thread from Background Thread
• how to pass URL query strings from one x
• Interactive Designer XBAP Questions
• NullReferenceException in the samples th
• Auto Scrolling text inside a listbox
• How to create irregular window from PNG
• Populating TreeView with xml file in c#?
• Bug Fixes
• Please help with package signing
• How is the control to which externals ar
• Problem using event of Timer to set text
• IsItemsHost triggerable?
• Custom RoutedEvents
• Custom control design: errors in template
• Need HELP getting started with WPF... (d

Hot Articles

• The size of Adorner nesting element
• Data bound ListView, Subscripts for Chem
• When to use windows browser app?
• Validating with Regular Expressions
• About event handler of overlay controls
• Control Rendering Space For a Line
• Problem:"Cannot locate resource 'wi
• Next .Net 3.0 release date, Orcas- When?
• ToolBar - OverflowGrid style?
• Updated answer to "How to bind to s
• Help me design my .Net 3.0 application?
• Whoops! (I'm getting tired of it)
• AutoScrolling Text in a ListBox?? Help P
• Animations at GDI speed?
• BitmapImage is not raising the DownloadC

Recommend Articles

• Controling RichTextBox elements' position
• Problems with Ink Analysis
• Writing reusable effects in XAML
• Windows Controls in Viewbox
• Post file in WinFX
• Disable repaint
• Help: RC1 ListView Memory Leak
• WPF equivalent for windows forms button.
• WPF observable Collection
• XamlReader? Codebehind classes don't work?
• Is it possible to implement a GridView w
• error when hosting WPF control inside of
• Minimum threshold for the Thumb
• Adorners: How to show stylable help and
• Access to an panel inside a ItemsPanelTe