MVVM - Test unitaire

L'idée derrière les tests unitaires est de prendre des morceaux discrets de code (unités) et d'écrire des méthodes de test qui utilisent le code de manière attendue, puis de tester pour voir si elles obtiennent les résultats attendus.

  • Étant eux-mêmes du code, les tests unitaires sont compilés comme le reste du projet.

  • Ils sont également exécutés par le logiciel de test, qui peut accélérer chaque test, donnant effectivement le pouce vers le haut ou le pouce vers le bas pour indiquer si le test a réussi ou échoué, respectivement.

Jetons un coup d'œil à un exemple créé précédemment. Voici la mise en œuvre du modèle étudiant.

using System.ComponentModel;

namespace MVVMDemo.Model {
 
   public class StudentModel {}
	
   public class Student : INotifyPropertyChanged { 
      private string firstName; 
      private string lastName;

      public string FirstName { 
         get { return firstName; }
			
         set { 
            if (firstName != value) { 
               firstName = value; 
               RaisePropertyChanged("FirstName");
               RaisePropertyChanged("FullName"); 
            } 
         }
      }

      public string LastName { 
         get { return lastName; } 
			
         set { 
            if (lastName != value) { 
               lastName = value; 
               RaisePropertyChanged("LastName");
               RaisePropertyChanged("FullName");
            } 
         } 
      }

      public string FullName { 
         get { 
            return firstName + " " + lastName; 
         } 
      }

      public event PropertyChangedEventHandler PropertyChanged;

      private void RaisePropertyChanged(string property) { 
         if (PropertyChanged != null) { 
            PropertyChanged(this, new PropertyChangedEventArgs(property)); 
         } 
      } 
   } 
}

Voici l'implémentation de StudentView.

<UserControl x:Class="MVVMDemo.Views.StudentView" 
   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" 
   xmlns:local = "clr-namespace:MVVMDemo.Views" 
   xmlns:viewModel = "clr-namespace:MVVMDemo.ViewModel" 
   xmlns:data = "clr-namespace:MVVMDemo.Model" 
   xmlns:vml = "clr-namespace:MVVMDemo.VML" 
   vml:ViewModelLocator.AutoHookedUpViewModel = "True" 
   mc:Ignorable = "d" 
   d:DesignHeight = "300" d:DesignWidth = "300">

   <UserControl.Resources> 
      <DataTemplate DataType = "{x:Type data:Student}"> 
		
         <StackPanel Orientation = "Horizontal"> 
            <TextBox Text = "{Binding Path = FirstName, Mode = TwoWay}" 
               Width = "100" Margin = "3 5 3 5"/> 
					
            <TextBox Text = "{Binding Path = LastName, Mode = TwoWay}" 
               Width = "100" Margin = "0 5 3 5"/> 
					
            <TextBlock Text = "{Binding Path = FullName, Mode = OneWay}" 
               Margin = "0 5 3 5"/> 
         </StackPanel> 
			
      </DataTemplate> 
   </UserControl.Resources>

   <Grid> 
      <StackPanel Orientation = "Horizontal"> 
         <ListBox ItemsSource = "{Binding Students}"
            SelectedItem = "{Binding SelectedStudent}"/> 
				
         <Button Content = "Delete" 
            Command = "{Binding DeleteCommand}" 
            HorizontalAlignment = "Left" 
            VerticalAlignment = "Top" 
            Width = "75" /> 
      </StackPanel> 
   </Grid>

</UserControl>

Voici l'implémentation de StudentViewModel.

using MVVMDemo.Model;
 
using System.Collections.ObjectModel; 
using System.Windows.Input; 
using System;

namespace MVVMDemo.ViewModel { 

   public class StudentViewModel { 
	
      public MyICommand DeleteCommand { get; set;}
		
      public StudentViewModel() { 
         LoadStudents(); 
         DeleteCommand = new MyICommand(OnDelete, CanDelete); 
      }

      public ObservableCollection<Student> Students { 
         get; 
         set; 
      }

      public void LoadStudents() { 
         ObservableCollection<Student> students = new ObservableCollection<Student>();

         students.Add(new Student { FirstName = "Mark", LastName = "Allain" }); 
         students.Add(new Student { FirstName = "Allen", LastName = "Brown" }); 
         students.Add(new Student { FirstName = "Linda", LastName = "Hamerski" });
			
         Students = students; 
      } 
		
      private Student _selectedStudent; 
		
      public Student SelectedStudent { 
         get { 
            return _selectedStudent; 
         } 
			
         set { 
            _selectedStudent = value;
            DeleteCommand.RaiseCanExecuteChanged(); 
         } 
      } 
		
      private void OnDelete() { 
         Students.Remove(SelectedStudent); 
      }

      private bool CanDelete() { 
         return SelectedStudent != null; 
      }
		
      public int GetStudentCount() { 
         return Students.Count; 
      } 
   } 
}

Voici le fichier MainWindow.xaml.

<Window x:Class = "MVVMDemo.MainWindow" 
   xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" 
   xmlns:d = "http://schemas.microsoft.com/expression/blend/2008" 
   xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006" 
   xmlns:local = "clr-namespace:MVVMDemo" 
   xmlns:views = "clr-namespace:MVVMDemo.Views" 
   mc:Ignorable = "d" 
   Title = "MainWindow" Height = "350" Width = "525">

   <Grid> 
      <views:StudentView x:Name = "StudentViewControl"/> 
   </Grid>
 
</Window>

Voici l'implémentation MyICommand, qui implémente l'interface ICommand.

using System; 
using System.Windows.Input;

namespace MVVMDemo { 

   public class MyICommand : ICommand { 
      Action _TargetExecuteMethod; 
      Func<bool> _TargetCanExecuteMethod;

      public MyICommand(Action executeMethod) { 
         _TargetExecuteMethod = executeMethod; 
      }

      public MyICommand(Action executeMethod, Func<bool> canExecuteMethod) { 
         _TargetExecuteMethod = executeMethod;
         _TargetCanExecuteMethod = canExecuteMethod; 
      }

      public void RaiseCanExecuteChanged() {
         CanExecuteChanged(this, EventArgs.Empty); 
      }
		
      bool ICommand.CanExecute(object parameter) { 
		
         if (_TargetCanExecuteMethod != null) { 
            return _TargetCanExecuteMethod();
         } 
			
         if (_TargetExecuteMethod != null) { 
            return true; 
         } 
			
         return false; 
      }
		
      // Beware - should use weak references if command instance lifetime
         is longer than lifetime of UI objects that get hooked up to command
			
      // Prism commands solve this in their implementation
		
      public event EventHandler CanExecuteChanged = delegate { };

      void ICommand.Execute(object parameter) { 
         if (_TargetExecuteMethod != null) { 
            _TargetExecuteMethod(); 
         } 
      } 
   }
}

Lorsque le code ci-dessus est compilé et exécuté, vous verrez la sortie suivante sur votre fenêtre principale.

Pour écrire un test unitaire pour l'exemple ci-dessus, ajoutons un nouveau projet de test à la solution.

Ajoutez une référence au projet par un clic droit sur Références.

Sélectionnez le projet existant et cliquez sur OK.

Ajoutons maintenant un test simple qui vérifiera le nombre d'étudiants comme indiqué dans le code suivant.

using System; 

using Microsoft.VisualStudio.TestTools.UnitTesting; 
using MVVMDemo.ViewModel;

namespace MVVMTest { 
   [TestClass] 
	
   public class UnitTest1 { 
      [TestMethod] 
		
      public void TestMethod1() { 
         StudentViewModel sViewModel = new StudentViewModel(); 
         int count = sViewModel.GetStudentCount();
         Assert.IsTrue(count == 3); 
      } 
   } 
}

Pour exécuter ce test, sélectionnez l'option de menu Test → Exécuter → Tous les tests.

Vous pouvez voir dans l'Explorateur de tests que le test est réussi, car dans le StudentViewModel, trois étudiants sont ajoutés. Modifiez la condition de comptage de 3 à 4 comme indiqué dans le code suivant.

using System; 

using Microsoft.VisualStudio.TestTools.UnitTesting; 
using MVVMDemo.ViewModel;

namespace MVVMTest { 
   [TestClass] 
	
   public class UnitTest1 { 
      [TestMethod] public void TestMethod1() {
         StudentViewModel sViewModel = new StudentViewModel(); 
         int count = sViewModel.GetStudentCount();
         Assert.IsTrue(count == 4);
      } 
   } 
}

Lorsque le plan de test est à nouveau exécuté, vous verrez que le test a échoué car le nombre d'élèves n'est pas égal à 4.

Nous vous recommandons d'exécuter l'exemple ci-dessus dans une méthode étape par étape pour une meilleure compréhension.