2011-11-12 24 views
6

Estoy intentando probar la unidad de un control WPF personalizado usando NUnit. El control es un ListView de botones, vinculado a una lista (el DataContext está configurado en el constructor del control).Unidad probando un enlace de datos en WPF

Me gustaría escribir pruebas que (por ejemplo) agreguen elementos a la lista y verifiquen que se agregue un nuevo botón a la vista, etc. Sin embargo, cuando agrego un elemento a la lista en mi prueba NUnit, todavía informa que el ListView está vacío. Sin embargo, todo funciona bien cuando ejecuto mi aplicación.

He incluido el código correspondiente a continuación. ¿Qué debo hacer para probar esto?

XAML:

<ListView x:Class="SoundBlock" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      ItemsSource="{Binding Path=Sounds}"> 
    <ListView.ItemTemplate> 
     <DataTemplate> 
      <Button Content="{Binding Title}" /> 
     </DataTemplate> 
    </ListView.ItemTemplate> 
</ListView> 

Definición de clase

public partial class SoundBlock : ListView 
{ 
    public SoundBlock(Board xiBoard) 
    { 
     // Initialize. 
     InitializeComponent(); 

     // Set the data context for this block. 
     DataContext = xiBoard; // Board has an ObservableCollection<Sound> 
           // property called Sounds. 
    } 
} 

caso de prueba

[Test] 
public void TestAddSound() 
{ 
    board = new Board(); 
    block = new SoundBlock(board); 
    Assert.AreEqual(0, block.Items.Count); 

    sound = new Sound("sound.mp3"); 
    board.Sounds.Add(sound); 
    Assert.AreEqual(1, block.Items.Count); // This fails - count is still 0 
} 

Respuesta

4

Ver mi pregunta: Force binding in WPF

Hay que forzar la unión, no funcionará hasta que se muestre el control. Puedes ponerlo en una nueva ventana y mostrar la ventana.

+0

Funciona muy bien. ¡Gracias! –

0

Ya que aglutinaba a su ItemSource a la Lista por lo que cualquier cambio en la colección no se propagará al control UI ya que List no implementa INotifyCollectionChanged y, por lo tanto, no se mostrarán cambios en la IU.

Puede insted use ObservableCollection<Sound> en lugar de List<Sound>. ObservableCollection implementa internamente la interfaz INotifyCollectioChanged.

+0

Probé eso - todavía no hay suerte, me temo! –

+0

Mientras se ejecuta la aplicación, que supongo que primero rellenar esta lista antes de la inicialización, por lo que su funcionamiento muy bien. Si intentas agregar cualquier elemento después de la inicialización del control, tampoco verás ningún botón adicional allí. –

+0

Lo siento, debería aclarar: he cambiado List a ObservableCollection y, en mi aplicación, agregué un elemento después de la inicialización. Esto funciona bien Sin embargo, la prueba de la unidad todavía está rota. –

3

No estoy seguro exactamente qué sucede y por qué funciona, pero si utiliza esta pequeña clase de ayuda para establecer el contexto de datos en el control que desea probar, entonces funciona.

public class DataContextHelper 
{ 
    public static void InjectDataContext(object element, object dataContext) 
    { 
     if (dataContext == null) 
      return; 
     if (element is FrameworkContentElement) 
      ((FrameworkContentElement)element).DataContext = dataContext; 
     else if (element is FrameworkElement) 
      ((FrameworkElement)element).DataContext = dataContext; 

     TriggerUpdateOfInMemoryView(); 
    } 

    /// <summary> 
    /// Triggers an update to a view that exists only in memory, not on the screen. 
    /// </summary> 
    /// <remarks> 
    /// When setting data context or modifying the control tree of a view that exists in 
    /// memory, then those changes are not automatically visible when e.g. attempting to 
    /// print the view. This function will trigger the update of the view so, e.g. a print 
    /// will display the updated view. 
    /// </remarks> 
    public static void TriggerUpdateOfInMemoryView() 
    { 
     var dispatcher = Dispatcher.CurrentDispatcher; 
     dispatcher.Invoke(
      DispatcherPriority.SystemIdle, 
      new DispatcherOperationCallback(arg => null), 
      null); 
    } 
} 
+0

Esto es brillante, pero ¿cuándo más debemos llamar 'TriggerUpdateOfInMemoryView'? Si el objetivo es probar que los enlaces cambian automáticamente las vistas, es una pena que tengamos que forzar manualmente las actualizaciones. –

+0

Esta es una buena solución. Mucho mejor que forzar una ventana. – on3al

Cuestiones relacionadas