2010-02-19 4 views
7

Je suis encore en train de grokking des comportements attachés en général, et je ne sais pas comment écrire un test unitaire.unité test un comportement ci-joint wpf

J'ai collé du code ci-dessous dans la structure Cinch de Sacha Barber qui permet à une fenêtre d'être fermée via un comportement attaché. Quelqu'un peut-il me montrer un exemple de test unitaire?

Merci!
Berryl

#region Close 

    /// <summary>Dependency property which holds the ICommand for the Close event</summary> 
    public static readonly DependencyProperty CloseProperty = 
     DependencyProperty.RegisterAttached("Close", 
      typeof(ICommand), typeof(Lifetime), 
       new UIPropertyMetadata(null, OnCloseEventInfoChanged)); 

    /// <summary>Attached Property getter to retrieve the CloseProperty ICommand</summary> 
    public static ICommand GetClose(DependencyObject source) 
    { 
     return (ICommand)source.GetValue(CloseProperty); 
    } 

    /// <summary>Attached Property setter to change the CloseProperty ICommand</summary> 
    public static void SetClose(DependencyObject source, ICommand command) 
    { 
     source.SetValue(CloseProperty, command); 
    } 

    /// <summary>This is the property changed handler for the Close property.</summary> 
    private static void OnCloseEventInfoChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) 
    { 
     var win = sender as Window; 
     if (win == null) return; 

     win.Closing -= OnWindowClosing; 
     win.Closed -= OnWindowClosed; 

     if (e.NewValue == null) return; 

     win.Closing += OnWindowClosing; 
     win.Closed += OnWindowClosed; 
    } 

    /// <summary> 
    /// This method is invoked when the Window.Closing event is raised. 
    /// It checks with the ICommand.CanExecute handler 
    /// and cancels the event if the handler returns false. 
    /// </summary> 
    private static void OnWindowClosing(object sender, CancelEventArgs e) 
    { 
     var dpo = (DependencyObject)sender; 
     var ic = GetClose(dpo); 
     if (ic == null) return; 

     e.Cancel = !ic.CanExecute(GetCommandParameter(dpo)); 
    } 

    /// <summary> 
    /// This method is invoked when the Window.Closed event is raised. 
    /// It executes the ICommand.Execute handler. 
    /// </summary> 
    static void OnWindowClosed(object sender, EventArgs e) 
    { 
     var dpo = (DependencyObject)sender; 
     var ic = GetClose(dpo); 
     if (ic == null) return; 

     ic.Execute(GetCommandParameter(dpo)); 
    } 

    #endregion 

Répondre

5

Vous utiliserais probablement un lambda dans votre ICommand en utilisant un DelegateCommand ou un RelayCommand. Des implémentations multiples de ceux-ci existent partout et Cinch peut avoir quelque chose de similaire. Version très simple (comme un exemple, non destiné à un usage de la production):

public class DelegateCommand : ICommand { 
    private Action _execute = null; 

    public void Execute(object parameter) { 
     _execute(); 
    } 

    public DelegateCommand(Action execute) { 
     _execute = execute; 
    } 

    #region stuff that doesn't affect functionality 
    public bool CanExecute(object parameter) { 
     return true; 
    } 
    public event EventHandler CanExecuteChanged { 
     add { } 
     remove { } 
    } 
    #endregion 
} 

Ensuite, votre corps de test pourrait ressembler à ceci:

bool wascalled = false; 

var execute = new DelegateCommand(
    () => { 
     wascalled = true; 
    }); 

var window = new Window(); 
SomeClass.SetClose(window, execute); 

// does the window need to be shown for Close() to work? Nope. 

window.Close(); 

AssertIsTrue(wascalled); 

Ceci est un exemple simpliste. Il y a bien sûr d'autres tests que vous voudrez effectuer, auquel cas vous devriez créer ou trouver une implémentation complète de DelegateCommand qui implémente aussi correctement CanExecute, entre autres choses.

+0

Cinch va encore plus loin avec RelayCommand en faisant cuire votre test wascalled dans une propriété CommandSucceeded bool. Votre message est utile pour faire en sorte que SetClose soit toujours un accesseur de propriété à la fin de la journée, même s'il ne ressemble pas à l'ensemble des paramètres normaux de la propriété C#! C'est l'une des choses que je ne voyais pas et je ne suis pas encore intuitif sur le comportement DP/attaché. Cheers – Berryl

+0

Oui. Une fois compilées, ces méthodes Get/Set statiques sont appelées. Même chose avec les DP: il ignore l'enveloppe des propriétés et appelle directement SetValue'/'GetValue' sur' DependencyObject'. C'est bon d'entendre ça à Cinch. Ce n'est pas celui que j'ai déjà lu. –

3

DependencyProperty changement et la coercition de valeur sur leurs propres regards comme 'Impossible' pour Dependencies moi. Avoir une référence à Window rend les choses encore plus compliquées. Je pense que je partirais avec Humble Object pattern ici ...

+0

Je peux réellement vivre avec la fenêtre dans ce cas, mais c'est un lien intéressant. Pouvez-vous illustrer comment appliquer le modèle d'objet humble dans ce cas? Vive – Berryl