gpt4 book ai didi

c# - WPF MVVM Light 单元测试 ViewModels

转载 作者:可可西里 更新时间:2023-11-01 08:42:04 26 4
gpt4 key购买 nike

我不是 MVVM 模式的常客,这基本上是我第一次使用它。

我过去所做的(“普通”WPF)是使用业务层和数据层(通常包含由服务或 Entity Framework 创建的实体)创建我的 View 。

现在经过一番尝试后,我从 MVVM Light 创建了一个标准模板并执行了以下操作:

定位器:

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

登录 View 模型:

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

绑定(bind)和命令工作正常,所以没有问题。设计和测试时间的业务模型类:

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 是一个静态类,它调用数据库( Entity Framework )。

现在我有我的测试:

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

现在我的测试不在 ViewModel 本身,而是直接到业务层。

基本上我有两个问题:

  • 我的道路是否正确,或者我的模式实现是否存在根本性缺陷?

  • 如何测试我的 ViewModel?

最佳答案

您的 View 模型有一段值得测试的相关代码,即 Login方法。鉴于它是私有(private)的,应该通过 LoginCommand 对其进行测试。 .

现在,有人可能会问,当您已经对底层业务逻辑进行了测试时,测试命令的目的是什么?目的是验证业务逻辑被调用并使用正确的参数

如何进行这样的测试?通过使用 mock .以 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();

通过这种方式,您可以验证命令是否按预期调用了业务层。注意 Action<bool>匹配参数时被忽略 - 很难匹配 Action<T>Func<T>通常不值得。

一些注意事项:

  • 您可能需要重新考虑在 View 模型中使用消息框代码(这应该属于 View , View 模型应该请求通知 View 以显示弹出窗口)。这样做,也将使更多的 View 模型测试成为可能(例如,不需要忽略 Action 参数)
  • 有人做测试INotifyPropertyChanged属性( UserName 在你的情况下) - 当属性值更改时引发该事件。由于这是很多样板代码,因此使用工具/library强烈建议将此过程自动化。
  • 您确实想要两组测试,一组用于 View 模型(如上例所示),另一组用于底层业务逻辑(您的原始测试)。在 MVVM 中,VM 是一个额外的层,它似乎没有什么用处 - 但这就是重点 - 那里没有业务逻辑,而是专注于 View 层的数据重新排列/准备。

关于c# - WPF MVVM Light 单元测试 ViewModels,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12332123/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com