2012-09-08 12 views
11

Tôi không thường xuyên với mẫu MVVM và đây là lần đầu tiên tôi chơi với nó.WPF MVVM Kiểm tra đơn vị ánh sáng ViewModels

Điều tôi thường làm (WPF "bình thường"), đã tạo Chế độ xem với lớp Doanh nghiệp và có thể là trình chứa dữ liệu (thường chứa các thực thể của tôi được tạo bởi dịch vụ hoặc Khung thực thể).

Bây giờ sau khi một số toying Tôi tạo ra một khuôn mẫu chuẩn từ MVVM Light và đã làm điều này:

Locator:

public class ViewModelLocator 
{ 
    static ViewModelLocator() 
    { 
     ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default); 

     if (ViewModelBase.IsInDesignModeStatic) 
     { 
      SimpleIoc.Default.Register<IUserService, DesignUserService>(); 
     } 
     else 
     { 
      SimpleIoc.Default.Register<IUserService, IUserService>(); 
     } 

     SimpleIoc.Default.Register<LoginViewModel>(); 
    } 

    public LoginViewModel Login 
    { 
     get 
     { 
      return ServiceLocator.Current.GetInstance<LoginViewModel>(); 
     } 
    } 
} 

Đăng nhập ViewModel:

public class LoginViewModel : ViewModelBase 
{ 
    private readonly IUserService _userService; 

    public RelayCommand<Object> LoginCommand 
    { 
     get 
     { 
      return new RelayCommand<Object>(Login); 
     } 
    } 

    private string _userName; 
    public String UserName 
    { 
     get { return _userName; } 
     set 
     { 
      if (value == _userName) 
       return; 

      _userName = value; 
      RaisePropertyChanged("UserName"); 
     } 
    } 

    /// <summary> 
    /// Initializes a new instance of the LoginViewModel class. 
    /// </summary> 
    public LoginViewModel(IUserService userService) 
    { 
     _userService = userService; 

     _closing = true; 
    } 

    private void Login(Object passwordBoxObject) 
    { 
     PasswordBox passwordBox = passwordBoxObject as PasswordBox; 
     if (passwordBox == null) 
      throw new Exception("PasswordBox is null"); 

     _userService.Login(UserName, passwordBox.SecurePassword, result => 
     { 
      if (!result) 
      { 
       MessageBox.Show("Wrong username or password"); 
      } 
     }); 
    } 
} 

Binding và các lệnh làm việc tốt do đó không có câu hỏi. Lớp mockup kinh doanh cho thiết kế và thời gian thử nghiệm:

public class DesignUserService : IUserService 
{ 
    private readonly User _testUser; 
    private readonly IList<User> _users; 

    public void Login(String userName, SecureString password, Action<Boolean> callback) 
    { 
     var user = _users.FirstOrDefault(u => u.UserName.ToLower() == userName.ToLower()); 

     if (user == null) 
     { 
      callback(false); 
      return; 
     } 

     String rawPassword = Security.ComputeHashString(password, user.Salt); 
     if (rawPassword != user.Password) 
     { 
      callback(false); 
      return; 
     } 

     callback(true); 
    } 

    public DesignUserService() 
    { 
     _testUser = new User 
     { 
      UserName = "testuser", 
      Password = "123123", 
      Salt = "123123" 
     }; 

     _users = new List<User> 
     { 
      _testUser 
     }; 
    } 
} 

UserData là lớp tĩnh gọi điện đến cơ sở dữ liệu (Entity Framework).

Bây giờ tôi có thử nghiệm của tôi:

[TestClass] 
public class Login 
{ 
    [TestMethod] 
    public void IncorrectUsernameCorrectPassword() 
    { 
     IUserService userService = new DesignUserService(); 

     PasswordBox passwordBox = new PasswordBox 
     { 
      Password = "password" 
     }; 
     userService.Login("nonexistingusername", passwordBox.SecurePassword, b => Assert.AreEqual(b, false)); 
    } 
} 

Bây giờ thử nghiệm của tôi không xuất hiện trên ViewModel bản thân nhưng trực tiếp đến lớp kinh doanh.

Về cơ bản tôi có 2 câu hỏi:

  • Am tôi trên con đường đúng đắn, hoặc là có thiếu sót cơ bản trong việc thực hiện mô hình của tôi?

  • Tôi làm cách nào để kiểm tra ViewModel của mình?

Trả lời

15

Mô hình chế độ xem của bạn có một đoạn mã có giá trị phù hợp, là phương pháp Login. Do đó là riêng tư, nên kiểm tra nó qua LoginCommand.

Bây giờ, người ta có thể hỏi, mục đích của việc kiểm tra lệnh khi bạn đã thử nghiệm cho logic nghiệp vụ cơ bản là gì? Mục đích là để xác minh rằng logic nghiệp vụ được gọi là và với thông số chính xác.

Làm cách nào để thực hiện thử nghiệm như vậy? Bằng cách sử dụng mock. Ví dụ với FakeItEasy:

var userServiceFake = A.Fake<IUserService>(); 
var testedViewModel = new LoginViewModel(userServiceFake); 

// prepare data for test 
var passwordBox = new PasswordBox { Password = "password" }; 
testedViewModel.UserName = "TestUser"; 

// execute test 
testedViewModel.LoginCommand.Execute(passwordBox); 

// verify 
A.CallTo(() => userServiceFake.Login(
    "TestUser", 
    passwordBox.SecurePassword, 
    A<Action<bool>>.Ignored) 
).MustHaveHappened(); 

Bằng cách này bạn xác minh lệnh đó gọi là lớp kinh doanh như mong đợi. Lưu ý rằng Action<bool> bị bỏ qua khi các thông số khớp nhau - rất khó để khớp với Action<T>Func<T> và thường không đáng giá.

vài lưu ý:

  • Bạn có thể muốn xem xét lại có mã hộp thông báo theo quan điểm mô hình (điều này phải thuộc về xem, xem mô hình nên một trong hai yêu cầu hoặc thông báo xem để hiển thị cửa sổ bật lên). Làm như vậy, cũng sẽ thực hiện nhiều hơn thông qua việc kiểm tra mô hình xem có thể (ví dụ:không cần phải bỏ qua đối số Action)
  • Một số người kiểm tra INotifyPropertyChanged thuộc tính (UserName trong trường hợp của bạn) - sự kiện đó được tăng lên khi giá trị thuộc tính thay đổi. Vì đây là rất nhiều mã soạn sẵn, sử dụng công cụ/library để tự động hóa quy trình này được đề xuất cao.
  • Bạn muốn có hai bộ kiểm tra, một cho mô hình xem (như trong ví dụ trên) và một cho logic nghiệp vụ cơ bản (thử nghiệm ban đầu của bạn). Trong MVVM, VM là lớp bổ sung mà dường như ít sử dụng - nhưng đó là toàn bộ vấn đề - không có logic nghiệp vụ ở đó và thay vì sắp xếp lại dữ liệu tập trung/chuẩn bị cho lớp khung nhìn.
+0

Cảm ơn câu trả lời của bạn! Nhưng tôi đang tìm kiếm, bạn vẫn đang thử nghiệm kết quả của lệnh hay chỉ khi lệnh được thực thi đúng cách? – YesMan85

+1

@ Rogier21: Bạn hiểu gì về * "kết quả của lệnh" *? Kết quả trực tiếp sẽ là một cuộc gọi đến lớp kinh doanh (được bao phủ với kiểm tra ở trên) - kết quả gián tiếp sẽ là bất kỳ mã lớp kinh doanh nào. Và điều đó nên được kiểm tra với các bài kiểm tra lớp nghiệp vụ (và theo như tôi thấy, bạn đã làm điều đó với thử nghiệm 'DesignUserService'). –

+0

Có, tôi thấy quan điểm của bạn. Bạn có nghĩa là bạn sẽ nhận được 2 bài kiểm tra: 1 để kiểm tra kết quả phù hợp của phương thức trong lớp nghiệp vụ (ví dụ Đăng nhập), và 1 bài kiểm tra để kiểm tra xem phương thức có được gọi chính xác từ ViewModel không? – YesMan85