Search code examples
wpfunit-testingattachedbehaviors

unit test an attached behavior wpf


I am still grokking attached behaviors in general, and am at a loss to see how to write a unit test for one.

I pasted some code below from Sacha Barber's Cinch framework that allows a window to be closed via attached behavior. Can somewone show me an example unit test for it?

Thanks!
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

Solution

  • You would likely use a lambda in your ICommand using a DelegateCommand or a RelayCommand. Multiple implementations of these exists all over the place and Cinch may have something similar. Really simple version (as an example, not meant for production use):

    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
    }
    

    Then your test body might look something like this:

    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 );
    

    This is an over-simplified example. There are of course other tests you'll want to perform, in which case you should create or find a fuller implementation of DelegateCommand that also properly implements CanExecute, among other things.