MVVM - การทดสอบหน่วย

แนวคิดเบื้องหลังการทดสอบหน่วยคือการใช้โค้ด (หน่วย) แบบแยกส่วนและเขียนวิธีการทดสอบที่ใช้โค้ดในลักษณะที่คาดหวังจากนั้นทดสอบเพื่อดูว่าได้ผลลัพธ์ตามที่คาดหวังหรือไม่

  • การทดสอบหน่วยจะถูกรวบรวมเช่นเดียวกับส่วนอื่น ๆ ของโครงการ

  • นอกจากนี้ยังดำเนินการโดยซอฟต์แวร์ทดสอบซึ่งสามารถเร่งความเร็วในการทดสอบแต่ละครั้งได้อย่างมีประสิทธิภาพยกนิ้วโป้งขึ้นหรือยกนิ้วลงเพื่อระบุว่าการทดสอบผ่านหรือล้มเหลวตามลำดับ

ลองมาดูตัวอย่างที่สร้างขึ้นก่อนหน้านี้ ต่อไปนี้คือการนำ Student Model ไปใช้

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>

ต่อไปนี้คือการใช้งาน MyICommand ซึ่งใช้อินเทอร์เฟซ 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(); 
         } 
      } 
   }
}

เมื่อโค้ดด้านบนถูกคอมไพล์และดำเนินการคุณจะเห็นผลลัพธ์ต่อไปนี้ในหน้าต่างหลักของคุณ

ในการเขียนการทดสอบหน่วยสำหรับตัวอย่างข้างต้นให้เพิ่มโครงการทดสอบใหม่ลงในโซลูชัน

เพิ่มการอ้างอิงไปยังโครงการโดยคลิกขวาที่การอ้างอิง

เลือกโครงการที่มีอยู่แล้วคลิกตกลง

ตอนนี้ให้เราเพิ่มแบบทดสอบง่ายๆซึ่งจะตรวจสอบจำนวนนักเรียนตามที่แสดงในรหัสต่อไปนี้

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

ในการดำเนินการทดสอบนี้ให้เลือกทดสอบ→เรียกใช้→ตัวเลือกเมนูการทดสอบทั้งหมด

คุณสามารถเห็นใน Test Explorer ว่าการทดสอบผ่านแล้วเนื่องจากใน StudentViewModel มีการเพิ่มนักเรียนสามคน เปลี่ยนเงื่อนไขการนับจาก 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

เราขอแนะนำให้คุณดำเนินการตามตัวอย่างข้างต้นด้วยวิธีการทีละขั้นตอนเพื่อความเข้าใจที่ดีขึ้น