c# - Unit Test that involves some UI element -


should writing unit tests following?

code:

public observablecollection<dxtabitem> tabs { get; private set; } public icommand customerscommand { get; private set; }  customerscommand = new delegatecommand(opencustomers);  private void opencustomers() {    var projectservice = new projectservice(project.filepath);    var vm = new customersviewmodel(projectservice);    addtab("customers", new customersview(vm)); }  public void addtab(string tabname, object content, bool allowhide = true) {     tabs.add(new dxtabitem { header = tabname, content = content }); } 

test:

[testmethod] public void customercommandaddstab() {     _vm.customerscommand.execute(null);     assert.areequal("customers", _vm.tabs[1].header); } 

xaml:

<dx:dxtabcontrol itemssource="{binding tabs}" /> 

i using tdd approach, working code, , passes tests locally, on server ci build fails test because view (customersview) has inside doesn't work. realized test, though simple breaking mvvm. writing ui code inside viewmodel referencing dxtabitems , new'ing view.

what correct approach this? should not write tests @ (and rely on automated testing) or should refactor somehow viewmodel contains no ui elements, tips on how should useful.

clarification:

the reason kind of design each tab contains different view, example customers tab contains customersview, yet tab contain different, in data , presentation. hard define mechanism allow in mvvm fashion. @ least answer not trivial.

if dxtabitem derived tabitem not mvvm, in mvvm never access view elements directly in view model. should doing instead creating view model tabs (e.g. tabviewmodel), change tabs observablecollection<tabviewmodel> , bind tab control's itemssource property create gui tabs themselves.

as ci failing, shouldn't ever creating gui elements (i.e. customersview) in unit tests. time you'd during integration testing, different kettle of fish. views should ever loosely coupled view model though mechanism of data-binding, should able both run , test entire application without creating single view object.

update: it's easy...once know how! :) there couple of different ways achieve you're trying 2 common approaches data templates , triggers.

with data templates rely on fact view models supposed represent logic behind gui. if have client tab , product tab (say) should have corresponding view models i.e. clientpage , productpage. may wish create base class these (e.g. tabviewmodel) in case view model collection observablecollection<tabviewmodel> explained above, otherwise make observablecollection<object>. either way use data templates specify view create each tab:

<datatemplate datatype="{x:type vm:clientpage>     <view:clientview /> </datatemplate>  <datatemplate datatype="{x:type vm:productpage>     <view:productview /> </datatemplate> 

listbox , other collection elements apply these data templates automatically, alternatively can specify listbox.itemtemplate explicitly , use contentcontrol needed.

the second method use data triggers. if pages fixed find helps create enumeration in view model layer reasons i'll explain in minute:

public enum pagetype : int {     client,     product,     ... etc ... } 

back in xaml you'll want create page each of these, can in vm if although it's such easy task in xaml:

<objectdataprovider methodname="getvalues" objecttype="{x:type sys:enum}" x:key="pagetype">         <objectdataprovider.methodparameters>             <x:type typename="vm:pagetype" />         </objectdataprovider.methodparameters>     </objectdataprovider> 

now can create tabcontrol , bind itemssource object , separate tab appear each item in enum:

    <tabcontrol itemssource="{binding source={staticresource pagetype}}"                 selectedindex="{binding currentpage, updatesourcetrigger=propertychanged, mode=twoway, converter={staticresource enumtointconverter}}"                 issynchronizedwithcurrentitem="true"> 

currentpage is, of course, property in mainviewmodel of type pagetype

    private pagetype _currentpage;     public pagetype currentpage     {         { return _currentpage; }         set { _currentpage = value; raisepropertychanged(); }     } 

xaml isn't smart enough deal enums you'll need code enumtointconverter converts between two:

public class enumtointconverter : ivalueconverter {     #region ivalueconverter members      public object convert(object value, type targettype, object parameter, system.globalization.cultureinfo culture)     {         return (int)value;     }      public object convertback(object value, type targettype, object parameter, system.globalization.cultureinfo culture)     {         return enum.toobject(targettype, value);     }      #endregion } 

using enum might seem bit more work needed mean view model code can set active page @ time doing `this.currentpage = pagetype.client'. handy in later stages of application might want have list of products somewhere else in application , want provide user button (say) opens product page. provides entire application lot of control on behaviour of tabs. of course means notification whenever user changes tabs (i.e. when this.currentpage changes value) can useful loading data on demand improve performance of application...it doesn't matter if change order of pages around in enum later because view model code checking against enum instead of integer page number!

the other thing haven't shown how display appropriate child content on each of pages, , said done data trigger in listbox item style:

        <tabcontrol.resources>              <style targettype="{x:type tabitem}" basedon="{staticresource {x:type tabitem}}">                 <style.triggers>                      <!-- client -->                     <datatrigger binding="{binding}" value="{x:static vm:pagetype.client}">                         <setter property="header" value="client" />                         <setter property="content">                             <setter.value>                                 <view:clientview datacontext="{binding elementname=parenttab, path=datacontext.clientpage"/>                             </setter.value>                         </setter>                     </datatrigger>                      <!-- product -->                     <datatrigger binding="{binding}" value="{x:static vm:pagetype.product}">                         <setter property="header" value="product" />                         <setter property="content">                             <setter.value>                                 <view:productview datacontext="{binding elementname=parenttab, path=datacontext.productpage"/>                             </setter.value>                         </setter>                     </datatrigger> 

as can see each datatrigger checking see enum datacontext has been set , setting it's own header , content accordingly.


Comments

Popular posts from this blog

get url and add instance to a model with prefilled foreign key :django admin -

css - Make div keyboard-scrollable in jQuery Mobile? -

ruby on rails - Seeing duplicate requests handled with Unicorn -