Mvvm-unit-testing

提供:Dev Guides
移動先:案内検索

MVVM –ユニットテスト

単体テストの背後にある考え方は、個別のコードチャンク(ユニット)を取得し、予想される方法でコードを使用するテストメソッドを記述し、予想される結果が得られるかどうかをテストすることです。

  • コード自体であるため、単体テストはプロジェクトの他の部分と同様にコンパイルされます。
  • また、テスト実行ソフトウェアによって実行されます。これにより、各テストが高速化され、テストが成功したか失敗したかを効果的に示すことができます。

先ほど作成した例を見てみましょう。 以下は、学生モデルの実装です。

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

以下は、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>

以下は、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;
      }
   }
}

以下は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>

以下は、ICommandインターフェイスを実装するMyICommandの実装です。

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

上記のコードをコンパイルして実行すると、メインウィンドウに次の出力が表示されます。

MVVMユニットテストメインウィンドウ

上記の例の単体テストを作成するには、ソリューションに新しいテストプロジェクトを追加しましょう。

新しいプロジェクトの追加

「参照」を右クリックして、プロジェクトへの参照を追加します。

新しい参照の追加

既存のプロジェクトを選択し、[OK]をクリックします。

参照マネージャーMVVMテスト

次のコードに示すように、学生数をチェックする簡単なテストを追加しましょう。

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

このテストを実行するには、テスト→実行→すべてのテストメニューオプションを選択します。

MVVMテストの実行

Student Explorerには3人の生徒が追加されているため、テストエクスプローラーでテストに合格したことがわかります。 次のコードに示すように、カウント条件を3から4に変更します。

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

テスト計画を再度実行すると、学生数が4でないためにテストが失敗したことがわかります。

MVVMテスト失敗

理解を深めるために、上記の例を段階的な方法で実行することをお勧めします。